summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/scxml/qscxmlecmascriptdatamodel.cpp3
-rw-r--r--src/scxml/qscxmlexecutablecontent.cpp359
-rw-r--r--src/scxml/qscxmlexecutablecontent.h17
-rw-r--r--src/scxml/qscxmlexecutablecontent_p.h380
-rw-r--r--src/scxml/qscxmlinvokableservice.cpp22
-rw-r--r--src/scxml/qscxmlinvokableservice.h67
-rw-r--r--src/scxml/qscxmlparser.cpp752
-rw-r--r--src/scxml/qscxmlparser_p.h13
-rw-r--r--src/scxml/qscxmlqstates.cpp410
-rw-r--r--src/scxml/qscxmlqstates.h222
-rw-r--r--src/scxml/qscxmlqstates_p.h114
-rw-r--r--src/scxml/qscxmlstatemachine.cpp1559
-rw-r--r--src/scxml/qscxmlstatemachine.h23
-rw-r--r--src/scxml/qscxmlstatemachine_p.h255
-rw-r--r--src/scxml/qscxmltabledata.cpp1014
-rw-r--r--src/scxml/qscxmltabledata.h12
-rw-r--r--src/scxml/qscxmltabledata_p.h136
-rw-r--r--src/scxml/scxml.pro6
-rw-r--r--tests/auto/compiled/tst_compiled.cpp11
-rw-r--r--tests/auto/scion/tst_scion.cpp7
-rw-r--r--tests/auto/statemachine/eventoccurred.scxml16
-rw-r--r--tests/auto/statemachine/tst_statemachine.cpp31
-rw-r--r--tools/qscxmlc/cppdatamodel.t42
-rw-r--r--tools/qscxmlc/data.t125
-rw-r--r--tools/qscxmlc/decl.t21
-rw-r--r--tools/qscxmlc/generator.cpp13
-rw-r--r--tools/qscxmlc/qscxmlc.pro3
-rw-r--r--tools/qscxmlc/scxmlcppdumper.cpp2037
-rw-r--r--tools/qscxmlc/scxmlcppdumper.h28
-rw-r--r--tools/qscxmlc/templates.qrc7
30 files changed, 3805 insertions, 3900 deletions
diff --git a/src/scxml/qscxmlecmascriptdatamodel.cpp b/src/scxml/qscxmlecmascriptdatamodel.cpp
index ceba661..452d781 100644
--- a/src/scxml/qscxmlecmascriptdatamodel.cpp
+++ b/src/scxml/qscxmlecmascriptdatamodel.cpp
@@ -225,9 +225,6 @@ public:
return q->tableData()->string(id);
}
- QScxmlStateMachine::BindingMethod dataBinding() const
- { return stateMachine()->dataBinding(); }
-
bool hasProperty(const QString &name) const
{ return dataModel.hasProperty(name); }
diff --git a/src/scxml/qscxmlexecutablecontent.cpp b/src/scxml/qscxmlexecutablecontent.cpp
index a11b4ca..84b508e 100644
--- a/src/scxml/qscxmlexecutablecontent.cpp
+++ b/src/scxml/qscxmlexecutablecontent.cpp
@@ -297,8 +297,10 @@ bool QScxmlExecutionEngine::step(Instructions &ip)
QString eventName = QStringLiteral("done.state.") + extraData.toString();
QScxmlEventBuilder event(stateMachine, eventName, doneData);
+ auto e = event();
+ e->setEventType(QScxmlEvent::InternalEvent);
qCDebug(qscxmlLog) << stateMachine << "submitting event" << eventName;
- stateMachine->submitEvent(event());
+ stateMachine->submitEvent(e);
return true;
}
@@ -309,359 +311,4 @@ bool QScxmlExecutionEngine::step(Instructions &ip)
}
#endif // BUILD_QSCXMLC
-Builder::Builder()
- : m_initialSetup(QScxmlExecutableContent::NoInstruction)
- , m_isCppDataModel(false)
-{
- m_activeSequences.reserve(4);
-}
-
-bool Builder::visit(DocumentModel::Send *node)
-{
- auto instr = m_instructions.add<Send>(Send::calculateExtraSize(node->params.size(), node->namelist.size()));
- instr->instructionLocation = createContext(QStringLiteral("send"));
- instr->event = addString(node->event);
- instr->eventexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("eventexpr"), node->eventexpr);
- instr->type = addString(node->type);
- instr->typeexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("typeexpr"), node->typeexpr);
- instr->target = addString(node->target);
- instr->targetexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("targetexpr"), node->targetexpr);
- instr->id = addString(node->id);
- instr->idLocation = addString(node->idLocation);
- instr->delay = addString(node->delay);
- instr->delayexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("delayexpr"), node->delayexpr);
- instr->content = addString(node->content);
- generate(&instr->namelist, node->namelist);
- generate(instr->params(), node->params);
- return false;
-}
-
-void Builder::visit(DocumentModel::Raise *node)
-{
- auto instr = m_instructions.add<Raise>();
- instr->event = addString(node->event);
-}
-
-void Builder::visit(DocumentModel::Log *node)
-{
- auto instr = m_instructions.add<Log>();
- instr->label = addString(node->label);
- instr->expr = createEvaluatorString(QStringLiteral("log"), QStringLiteral("expr"), node->expr);
-}
-
-void Builder::visit(DocumentModel::Script *node)
-{
- auto instr = m_instructions.add<JavaScript>();
- instr->go = createEvaluatorVoid(QStringLiteral("script"), QStringLiteral("source"), node->content);
-}
-
-void Builder::visit(DocumentModel::Assign *node)
-{
- auto instr = m_instructions.add<Assign>();
- auto ctxt = createContext(QStringLiteral("assign"), QStringLiteral("expr"), node->expr);
- instr->expression = addAssignment(node->location, node->expr, ctxt);
-}
-
-bool Builder::visit(DocumentModel::If *node)
-{
- auto instr = m_instructions.add<If>(node->conditions.size());
- instr->conditions.count = node->conditions.size();
- auto it = instr->conditions.data();
- QString tag = QStringLiteral("if");
- for (int i = 0, ei = node->conditions.size(); i != ei; ++i) {
- *it++ = createEvaluatorBool(tag, QStringLiteral("cond"), node->conditions.at(i));
- if (i == 0) {
- tag = QStringLiteral("elif");
- }
- }
- auto outSequences = m_instructions.add<InstructionSequences>();
- generate(outSequences, node->blocks);
- return false;
-}
-
-bool Builder::visit(DocumentModel::Foreach *node)
-{
- auto instr = m_instructions.add<Foreach>();
- auto ctxt = createContextString(QStringLiteral("foreach"));
- instr->doIt = addForeach(node->array, node->item, node->index, ctxt);
- startSequence(&instr->block);
- visit(&node->block);
- endSequence();
- return false;
-}
-
-void Builder::visit(DocumentModel::Cancel *node)
-{
- auto instr = m_instructions.add<Cancel>();
- instr->sendid = addString(node->sendid);
- instr->sendidexpr = createEvaluatorString(QStringLiteral("cancel"), QStringLiteral("sendidexpr"), node->sendidexpr);
-}
-
-ContainerId Builder::generate(const DocumentModel::DoneData *node)
-{
- auto id = m_instructions.newContainerId();
- DoneData *doneData;
- if (node) {
- doneData = m_instructions.add<DoneData>(node->params.size() * Param::calculateSize());
- doneData->contents = addString(node->contents);
- doneData->expr = createEvaluatorString(QStringLiteral("donedata"), QStringLiteral("expr"), node->expr);
- generate(&doneData->params, node->params);
- } else {
- doneData = m_instructions.add<DoneData>();
- doneData->contents = NoString;
- doneData->expr = NoEvaluator;
- doneData->params.count = 0;
- }
- doneData->location = createContext(QStringLiteral("final"));
- return id;
-}
-
-StringId Builder::createContext(const QString &instrName)
-{
- return addString(createContextString(instrName));
-}
-
-void Builder::generate(const QVector<DocumentModel::DataElement *> &dataElements)
-{
- foreach (DocumentModel::DataElement *el, dataElements) {
- auto ctxt = createContext(QStringLiteral("data"), QStringLiteral("expr"), el->expr);
- auto evaluator = addDataElement(el->id, el->expr, ctxt);
- if (evaluator != NoEvaluator) {
- auto instr = m_instructions.add<QScxmlExecutableContent::Initialize>();
- instr->expression = evaluator;
- }
- }
-}
-
-ContainerId Builder::generate(const DocumentModel::InstructionSequences &inSequences)
-{
- if (inSequences.isEmpty())
- return NoInstruction;
-
- auto id = m_instructions.newContainerId();
- auto outSequences = m_instructions.add<InstructionSequences>();
- generate(outSequences, inSequences);
- return id;
-}
-
-void Builder::generate(Array<Param> *out, const QVector<DocumentModel::Param *> &in)
-{
- out->count = in.size();
- Param *it = out->data();
- foreach (DocumentModel::Param *f, in) {
- it->name = addString(f->name);
- it->expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"), f->expr);
- it->location = addString(f->location);
- ++it;
- }
-}
-
-void Builder::generate(InstructionSequences *outSequences, const DocumentModel::InstructionSequences &inSequences)
-{
- int sequencesOffset = m_instructions.offset(outSequences);
- int sequenceCount = 0;
- int entryCount = 0;
- foreach (DocumentModel::InstructionSequence *sequence, inSequences) {
- ++sequenceCount;
- startNewSequence();
- visit(sequence);
- entryCount += endSequence()->size();
- }
- outSequences = m_instructions.at<InstructionSequences>(sequencesOffset);
- outSequences->sequenceCount = sequenceCount;
- outSequences->entryCount = entryCount;
-}
-
-void Builder::generate(Array<StringId> *out, const QStringList &in)
-{
- out->count = in.size();
- StringId *it = out->data();
- foreach (const QString &str, in) {
- *it++ = addString(str);
- }
-}
-
-ContainerId Builder::startNewSequence()
-{
- auto id = m_instructions.newContainerId();
- auto sequence = m_instructions.add<InstructionSequence>();
- startSequence(sequence);
- return id;
-}
-
-void Builder::startSequence(InstructionSequence *sequence)
-{
- SequenceInfo info;
- info.location = m_instructions.offset(sequence);
- info.entryCount = 0;
- m_activeSequences.push_back(info);
- m_instructions.setSequenceInfo(&m_activeSequences.last());
- sequence->instructionType = Instruction::Sequence;
- sequence->entryCount = -1; // checked in endSequence
-}
-
-InstructionSequence *Builder::endSequence()
-{
- SequenceInfo info = m_activeSequences.back();
- m_activeSequences.pop_back();
- m_instructions.setSequenceInfo(m_activeSequences.isEmpty() ? Q_NULLPTR : &m_activeSequences.last());
-
- auto sequence = m_instructions.at<InstructionSequence>(info.location);
- Q_ASSERT(sequence->entryCount == -1); // set in startSequence
- sequence->entryCount = info.entryCount;
- if (!m_activeSequences.isEmpty())
- m_activeSequences.last().entryCount += info.entryCount;
- return sequence;
-}
-
-EvaluatorId Builder::createEvaluatorString(const QString &instrName, const QString &attrName, const QString &expr)
-{
- if (!expr.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_stringEvaluators.insert(id, expr);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, expr);
- return addEvaluator(expr, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-EvaluatorId Builder::createEvaluatorBool(const QString &instrName, const QString &attrName, const QString &cond)
-{
- if (!cond.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_boolEvaluators.insert(id, cond);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, cond);
- return addEvaluator(cond, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-EvaluatorId Builder::createEvaluatorVariant(const QString &instrName, const QString &attrName, const QString &expr)
-{
- if (!expr.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_variantEvaluators.insert(id, expr);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, expr);
- return addEvaluator(expr, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-EvaluatorId Builder::createEvaluatorVoid(const QString &instrName, const QString &attrName, const QString &stuff)
-{
- if (!stuff.isEmpty()) {
- if (isCppDataModel()) {
- auto id = m_evaluators.add(EvaluatorInfo(), false);
- m_voidEvaluators.insert(id, stuff);
- return id;
- } else {
- QString loc = createContext(instrName, attrName, stuff);
- return addEvaluator(stuff, loc);
- }
- }
-
- return NoEvaluator;
-}
-
-DynamicTableData *Builder::tableData()
-{
- auto td = new DynamicTableData;
- td->strings = m_stringTable.data();
- td->theInstructions = m_instructions.data();
- td->theEvaluators = m_evaluators.data();
- td->theAssignments = m_assignments.data();
- td->theForeaches = m_foreaches.data();
- td->theDataNameIds = m_dataIds;
- td->theInitialSetup = m_initialSetup;
- td->theName = m_name;
- return td;
-}
-
-QString DynamicTableData::string(StringId id) const
-{
- return id == NoString ? QString() : strings.at(id);
-}
-
-Instructions DynamicTableData::instructions() const
-{
- return const_cast<Instructions>(theInstructions.data());
-}
-
-EvaluatorInfo DynamicTableData::evaluatorInfo(EvaluatorId evaluatorId) const
-{
- return theEvaluators[evaluatorId];
-}
-
-AssignmentInfo DynamicTableData::assignmentInfo(EvaluatorId assignmentId) const
-{
- return theAssignments[assignmentId];
-}
-
-ForeachInfo DynamicTableData::foreachInfo(EvaluatorId foreachId) const
-{
- return theForeaches[foreachId];
-}
-
-StringId *DynamicTableData::dataNames(int *count) const
-{
- Q_ASSERT(count);
- *count = theDataNameIds.size();
- return const_cast<StringId *>(theDataNameIds.data());
-}
-
-ContainerId DynamicTableData::initialSetup() const
-{
- return theInitialSetup;
-}
-
-QString DynamicTableData::name() const
-{
- return theName;
-}
-
-QVector<qint32> DynamicTableData::instructionTable() const
-{
- return theInstructions;
-}
-
-QVector<QString> DynamicTableData::stringTable() const
-{
- return strings;
-}
-
-QVector<EvaluatorInfo> DynamicTableData::evaluators() const
-{
- return theEvaluators;
-}
-
-QVector<AssignmentInfo> DynamicTableData::assignments() const
-{
- return theAssignments;
-}
-
-QVector<ForeachInfo> DynamicTableData::foreaches() const
-{
- return theForeaches;
-}
-
-StringIds DynamicTableData::allDataNameIds() const
-{
- return theDataNameIds;
-}
-
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlexecutablecontent.h b/src/scxml/qscxmlexecutablecontent.h
index 4bcfc36..969b1f9 100644
--- a/src/scxml/qscxmlexecutablecontent.h
+++ b/src/scxml/qscxmlexecutablecontent.h
@@ -83,6 +83,23 @@ struct ForeachInfo {
typedef qint32 EvaluatorId;
enum { NoEvaluator = -1 };
+
+struct Q_SCXML_EXPORT Param
+{
+ StringId name;
+ EvaluatorId expr;
+ StringId location;
+
+ Param(StringId theName, EvaluatorId theExpr, StringId theLocation)
+ : name(theName), expr(theExpr), location(theLocation)
+ {}
+
+ Param() : name(NoString), expr(NoInstruction), location(NoString)
+ {}
+
+ static int calculateSize() { return sizeof(Param) / sizeof(qint32); }
+};
+
} // QScxmlExecutableContent namespace
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlexecutablecontent_p.h b/src/scxml/qscxmlexecutablecontent_p.h
index 0d2e286..f7a4696 100644
--- a/src/scxml/qscxmlexecutablecontent_p.h
+++ b/src/scxml/qscxmlexecutablecontent_p.h
@@ -52,10 +52,9 @@
//
#include <QtScxml/qscxmlexecutablecontent.h>
-#include <QtScxml/qscxmltabledata.h>
+#include <QtScxml/private/qscxmltabledata_p.h>
#include <QtScxml/private/qscxmlparser_p.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qvariant.h>
+#include <QTextStream>
#ifndef BUILD_QSCXMLC
#include <QtScxml/qscxmldatamodel.h>
@@ -109,15 +108,6 @@ struct Array
int size() const { return sizeof(Array<T>) / sizeof(qint32) + dataSize(); }
};
-struct Q_SCXML_EXPORT Param
-{
- StringId name;
- EvaluatorId expr;
- StringId location;
-
- static int calculateSize() { return sizeof(Param) / sizeof(qint32); }
-};
-
struct Q_SCXML_EXPORT Instruction
{
enum InstructionType: qint32 {
@@ -277,245 +267,195 @@ struct Q_SCXML_EXPORT Cancel: Instruction
int size() const { return sizeof(Cancel) / sizeof(qint32); }
};
-#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
-#pragma pack(pop)
-#endif
-
-class Q_SCXML_EXPORT DynamicTableData:
-#ifndef BUILD_QSCXMLC
- public QObject,
-#endif // BUILD_QSCXMLC
- public QScxmlTableData
-{
-#ifndef BUILD_QSCXMLC
- Q_OBJECT
-#endif
-
-public:
- QString string(StringId id) const Q_DECL_OVERRIDE;
- Instructions instructions() const Q_DECL_OVERRIDE;
- EvaluatorInfo evaluatorInfo(EvaluatorId evaluatorId) const Q_DECL_OVERRIDE;
- AssignmentInfo assignmentInfo(EvaluatorId assignmentId) const Q_DECL_OVERRIDE;
- ForeachInfo foreachInfo(EvaluatorId foreachId) const Q_DECL_OVERRIDE;
- StringId *dataNames(int *count) const Q_DECL_OVERRIDE;
- ContainerId initialSetup() const Q_DECL_OVERRIDE;
- QString name() const Q_DECL_OVERRIDE;
-
- QVector<qint32> instructionTable() const;
- QVector<QString> stringTable() const;
- QVector<EvaluatorInfo> evaluators() const;
- QVector<AssignmentInfo> assignments() const;
- QVector<ForeachInfo> foreaches() const;
- StringIds allDataNameIds() const;
-
-private:
- friend class Builder;
- QVector<QString> strings;
- QVector<qint32> theInstructions;
- QVector<EvaluatorInfo> theEvaluators;
- QVector<AssignmentInfo> theAssignments;
- QVector<ForeachInfo> theForeaches;
- StringIds theDataNameIds;
- EvaluatorId theInitialSetup;
- QString theName;
-};
-
-class Q_SCXML_EXPORT Builder: public DocumentModel::NodeVisitor
-{
-public:
- Builder();
-
-protected: // visitor
- using NodeVisitor::visit;
-
- bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Raise *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Log *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Script *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Assign *node) Q_DECL_OVERRIDE;
- bool visit(DocumentModel::If *node) Q_DECL_OVERRIDE;
- bool visit(DocumentModel::Foreach *node) Q_DECL_OVERRIDE;
- void visit(DocumentModel::Cancel *node) Q_DECL_OVERRIDE;
-
-protected:
- ContainerId generate(const DocumentModel::DoneData *node);
- StringId createContext(const QString &instrName);
- void generate(const QVector<DocumentModel::DataElement *> &dataElements);
- ContainerId generate(const DocumentModel::InstructionSequences &inSequences);
- void generate(Array<Param> *out, const QVector<DocumentModel::Param *> &in);
- void generate(InstructionSequences *outSequences, const DocumentModel::InstructionSequences &inSequences);
- void generate(Array<StringId> *out, const QStringList &in);
- ContainerId startNewSequence();
- void startSequence(InstructionSequence *sequence);
- InstructionSequence *endSequence();
- EvaluatorId createEvaluatorString(const QString &instrName, const QString &attrName, const QString &expr);
- EvaluatorId createEvaluatorBool(const QString &instrName, const QString &attrName, const QString &cond);
- EvaluatorId createEvaluatorVariant(const QString &instrName, const QString &attrName, const QString &expr);
- EvaluatorId createEvaluatorVoid(const QString &instrName, const QString &attrName, const QString &stuff);
-
- virtual QString createContextString(const QString &instrName) const = 0;
- virtual QString createContext(const QString &instrName, const QString &attrName, const QString &attrValue) const = 0;
-
- DynamicTableData *tableData();
-
- StringId addString(const QString &str)
- { return str.isEmpty() ? NoString : m_stringTable.add(str); }
-
- void setInitialSetup(ContainerId id)
- { m_initialSetup = id; }
-
- void setName(const QString &name)
- { m_name = name; }
+struct StateTable {
+ int version;
+ int name;
+ enum: int {
+ InvalidDataModel = -1,
+ NullDataModel = 0,
+ EcmaScriptDataModel = 1,
+ CppDataModel = 2
+ } dataModel;
+ int childStates; // offset into offsets
+ int initialTransition;
+ int initialSetup;
+ enum: int { InvalidBinding = -1, EarlyBinding = 0, LateBinding = 1 } binding;
+ int maxServiceId;
+ int stateOffset, stateCount;
+ int transitionOffset, transitionCount;
+ int arrayOffset, arraySize;
+
+ enum { terminator = 0xc0ff33 };
+ enum { InvalidIndex = -1 };
+
+ struct State {
+ int name;
+ int parent;
+ enum: int {
+ Invalid = -1,
+ Normal = 0,
+ Parallel = 1,
+ Final = 2,
+ ShallowHistory = 3,
+ DeepHistory = 4
+ } type;
+ int initialTransition;
+ int initInstructions;
+ int entryInstructions;
+ int exitInstructions;
+ int doneData;
+ int childStates; // offset into arrays
+ int transitions; // offset into arrays
+ int serviceFactoryIds; // offset into arrays
+
+ State()
+ : name(InvalidIndex)
+ , parent(InvalidIndex)
+ , type(Invalid)
+ , initialTransition(InvalidIndex)
+ , initInstructions(InvalidIndex)
+ , entryInstructions(InvalidIndex)
+ , exitInstructions(InvalidIndex)
+ , doneData(InvalidIndex)
+ , childStates(InvalidIndex)
+ , transitions(InvalidIndex)
+ , serviceFactoryIds(InvalidIndex)
+ {}
- bool isCppDataModel() const
- { return m_isCppDataModel; }
+ bool isAtomic() const
+ { return childStates == InvalidIndex; }
- void setIsCppDataModel(bool onoff)
- { m_isCppDataModel = onoff; }
+ bool isCompound() const
+ { return type == Normal && childStates != InvalidIndex; }
- QMap<EvaluatorId, QString> boolEvaluators() const
- { return m_boolEvaluators; }
+ bool parentIsScxmlElement() const
+ { return parent == InvalidIndex; }
- QMap<EvaluatorId, QString> stringEvaluators() const
- { return m_stringEvaluators; }
+ bool isHistoryState() const
+ { return type == ShallowHistory || type == DeepHistory; }
- QMap<EvaluatorId, QString> variantEvaluators() const
- { return m_variantEvaluators; }
+ bool isParallel() const
+ { return type == Parallel; }
+ };
- QMap<EvaluatorId, QString> voidEvaluators() const
- { return m_voidEvaluators; }
+ struct Transition {
+ int events; // offset into offsets
+ int condition;
+ enum: int {
+ Invalid = -1,
+ External = 0,
+ Internal = 1
+ } type;
+ int source;
+ int targets; // offset into offsets
+ int transitionInstructions;
+
+ Transition()
+ : events(InvalidIndex)
+ , condition(InvalidIndex)
+ , type(Invalid)
+ , source(InvalidIndex)
+ , targets(InvalidIndex)
+ , transitionInstructions(InvalidIndex)
+ {}
+ };
-private:
- template <typename T, typename U>
- class Table {
- QVector<T> elements;
- QMap<T, int> indexForElement;
-
- public:
- U add(const T &s, bool uniqueOnly = true) {
- int pos = uniqueOnly ? indexForElement.value(s, -1) : -1;
- if (pos == -1) {
- pos = elements.size();
- elements.append(s);
- indexForElement.insert(s, pos);
- }
- return pos;
- }
+ struct Array {
+ Array(const int *start): start(start) {}
+ int size() const { return *start; }
+ bool isValid() const { return start != nullptr; }
- QVector<T> data() {
- elements.squeeze();
- return elements;
+ int operator[](int idx) const {
+ Q_ASSERT(idx >= 0);
+ Q_ASSERT(idx < size());
+ return *(start + idx + 1);
}
- };
- Table<QString, StringId> m_stringTable;
- struct SequenceInfo {
- int location;
- qint32 entryCount; // the amount of qint32's that the instructions take up
- };
- QVector<SequenceInfo> m_activeSequences;
+ struct const_iterator: public std::iterator<std::forward_iterator_tag, int, ptrdiff_t,
+ const int *, const int &>
+ {
+ const_iterator(const Array &a, int pos): a(a), pos(pos) {}
- class InstructionStorage {
- public:
- InstructionStorage()
- : m_info(Q_NULLPTR)
- {}
+ const_iterator &operator++() {
+ if (pos < a.size()) ++pos;
+ return *this;
+ }
- ContainerId newContainerId() const { return m_instr.size(); }
+ bool operator==(const const_iterator &other) const
+ { return &other.a == &a && other.pos == pos; }
- template <typename T>
- T *add(int extra = 0)
- {
- int pos = m_instr.size();
- int size = sizeof(T) / sizeof(qint32) + extra;
- if (m_info)
- m_info->entryCount += size;
- m_instr.resize(pos + size);
- T *instr = at<T>(pos);
- Q_ASSERT(instr->instructionType == 0);
- instr->instructionType = T::kind();
- return instr;
- }
+ bool operator!=(const StateTable::Array::const_iterator &other)
+ { return !this->operator==(other); }
- int offset(Instruction *instr) const
- {
- return reinterpret_cast<qint32 *>(instr) - m_instr.data();
- }
+ int operator*() const {
+ if (pos < a.size())
+ return a[pos];
+ else
+ return -1;
+ }
- template <typename T>
- T *at(int offset)
- {
- return reinterpret_cast<T *>(&m_instr[offset]);
- }
+ private:
+ const Array &a;
+ int pos;
+ };
- QVector<qint32> data()
- {
- m_instr.squeeze();
- return m_instr;
- }
+ const_iterator begin() const
+ { return const_iterator(*this, 0); }
- void setSequenceInfo(SequenceInfo *info)
- {
- m_info = info;
- }
+ const_iterator end() const
+ { return const_iterator(*this, size()); }
private:
- QVector<qint32> m_instr;
- SequenceInfo *m_info;
+ const int *start;
};
- InstructionStorage m_instructions;
-
- EvaluatorId addEvaluator(const QString &expr, const QString &context)
- {
- EvaluatorInfo ei;
- ei.expr = addString(expr);
- ei.context = addString(context);
- return m_evaluators.add(ei);
- }
- EvaluatorId addAssignment(const QString &dest, const QString &expr, const QString &context)
+ StateTable()
+ : version(InvalidIndex)
+ , name(InvalidIndex)
+ , dataModel(InvalidDataModel)
+ , childStates(InvalidIndex)
+ , initialTransition(InvalidIndex)
+ , initialSetup(InvalidIndex)
+ , binding(InvalidBinding)
+ , maxServiceId(InvalidIndex)
+ , stateOffset(InvalidIndex), stateCount(InvalidIndex)
+ , transitionOffset(InvalidIndex), transitionCount(InvalidIndex)
+ , arrayOffset(InvalidIndex), arraySize(InvalidIndex)
+ {}
+
+ const State &state(int idx) const
{
- AssignmentInfo ai;
- ai.dest = addString(dest);
- ai.expr = addString(expr);
- ai.context = addString(context);
- return m_assignments.add(ai);
+ Q_ASSERT(idx >= 0);
+ Q_ASSERT(idx < stateCount);
+ return reinterpret_cast<const State *>(
+ reinterpret_cast<const int *>(this) + stateOffset)[idx];
}
- EvaluatorId addForeach(const QString &array, const QString &item, const QString &index, const QString &context)
+ const Transition &transition(int idx) const
{
- ForeachInfo fi;
- fi.array = addString(array);
- fi.item = addString(item);
- fi.index = addString(index);
- fi.context = addString(context);
- return m_foreaches.add(fi);
+ Q_ASSERT(idx >= 0);
+ Q_ASSERT(idx < transitionCount);
+ return reinterpret_cast<const Transition *>(
+ reinterpret_cast<const int *>(this) + transitionOffset)[idx];
}
- EvaluatorId addDataElement(const QString &id, const QString &expr, const QString &context)
+ const Array array(int idx) const
{
- auto str = addString(id);
- if (!m_dataIds.contains(str))
- m_dataIds.append(str);
- if (expr.isEmpty())
- return NoEvaluator;
-
- return addAssignment(id, expr, context);
+ Q_ASSERT(idx < arraySize);
+ if (idx >= 0) {
+ const int *start = reinterpret_cast<const int *>(this) + arrayOffset + idx;
+ Q_ASSERT(*start + idx < arraySize);
+ return Array(start);
+ } else {
+ return Array(nullptr);
+ }
}
-
- Table<EvaluatorInfo, EvaluatorId> m_evaluators;
- Table<AssignmentInfo, EvaluatorId> m_assignments;
- Table<ForeachInfo, EvaluatorId> m_foreaches;
- QMap<EvaluatorId, QString> m_boolEvaluators;
- QMap<EvaluatorId, QString> m_stringEvaluators;
- QMap<EvaluatorId, QString> m_variantEvaluators;
- QMap<EvaluatorId, QString> m_voidEvaluators;
- StringIds m_dataIds;
- ContainerId m_initialSetup;
- QString m_name;
- bool m_isCppDataModel;
};
+#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
+#pragma pack(pop)
+#endif
+
class QScxmlExecutionEngine
{
Q_DISABLE_COPY(QScxmlExecutionEngine)
diff --git a/src/scxml/qscxmlinvokableservice.cpp b/src/scxml/qscxmlinvokableservice.cpp
index bb29635..16311ca 100644
--- a/src/scxml/qscxmlinvokableservice.cpp
+++ b/src/scxml/qscxmlinvokableservice.cpp
@@ -104,7 +104,7 @@ public:
QScxmlExecutableContent::StringId idlocation,
const QVector<QScxmlExecutableContent::StringId> &namelist,
bool autoforward,
- const QVector<QScxmlInvokableServiceFactory::Param> &params,
+ const QVector<QScxmlExecutableContent::Param> &params,
QScxmlExecutableContent::ContainerId finalize)
: invokeLocation(invokeLocation)
, srcexpr(srcexpr)
@@ -123,7 +123,7 @@ public:
QScxmlExecutableContent::StringId idPrefix;
QScxmlExecutableContent::StringId idlocation;
QVector<QScxmlExecutableContent::StringId> namelist;
- QVector<QScxmlInvokableServiceFactory::Param> params;
+ QVector<QScxmlExecutableContent::Param> params;
QScxmlExecutableContent::ContainerId finalize;
bool autoforward;
};
@@ -136,7 +136,7 @@ QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory(
QScxmlExecutableContent::StringId idlocation,
const QVector<QScxmlExecutableContent::StringId> &namelist,
bool autoforward,
- const QVector<Param> &params,
+ const QVector<QScxmlExecutableContent::Param> &params,
QScxmlExecutableContent::ContainerId finalize)
: d(new Data(invokeLocation,
srcexpr,
@@ -202,7 +202,7 @@ QVariantMap QScxmlInvokableServiceFactory::calculateData(QScxmlStateMachine *par
auto dataModel = parent->dataModel();
auto tableData = parent->tableData();
- foreach (const Param &param, d->params) {
+ foreach (const QScxmlExecutableContent::Param &param, d->params) {
auto name = tableData->string(param.name);
if (param.expr != QScxmlExecutableContent::NoEvaluator) {
@@ -326,10 +326,10 @@ QScxmlInvokableScxmlServiceFactory::QScxmlInvokableScxmlServiceFactory(
QScxmlExecutableContent::StringId idlocation,
const QVector<QScxmlExecutableContent::StringId> &namelist,
bool doAutoforward,
- const QVector<QScxmlInvokableServiceFactory::Param> &params,
+ const QVector<QScxmlExecutableContent::Param> &params,
QScxmlExecutableContent::ContainerId finalize)
: QScxmlInvokableServiceFactory(invokeLocation, srcexpr, id, idPrefix, idlocation, namelist,
- doAutoforward, params, finalize)
+ doAutoforward, params, finalize)
{}
QScxmlInvokableService *QScxmlInvokableScxmlServiceFactory::finishInvoke(QScxmlStateMachine *child, QScxmlStateMachine *parent)
@@ -338,4 +338,14 @@ QScxmlInvokableService *QScxmlInvokableScxmlServiceFactory::finishInvoke(QScxmlS
return new QScxmlInvokableScxml(this, child, parent);
}
+QScxmlInvokableService *QScxmlDynamicScxmlFactory::invoke(QScxmlStateMachine *parent)
+{
+ bool ok = true;
+ auto srcexpr = calculateSrcexpr(parent, &ok);
+ if (!ok)
+ return Q_NULLPTR;
+
+ return loadAndInvokeDynamically(parent, srcexpr);
+}
+
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlinvokableservice.h b/src/scxml/qscxmlinvokableservice.h
index 850047a..8323fe2 100644
--- a/src/scxml/qscxmlinvokableservice.h
+++ b/src/scxml/qscxmlinvokableservice.h
@@ -78,27 +78,6 @@ private:
class Q_SCXML_EXPORT QScxmlInvokableServiceFactory
{
public:
- struct Q_SCXML_EXPORT Param
- {
- QScxmlExecutableContent::StringId name;
- QScxmlExecutableContent::EvaluatorId expr;
- QScxmlExecutableContent::StringId location;
-
- Param(QScxmlExecutableContent::StringId theName,
- QScxmlExecutableContent::EvaluatorId theExpr,
- QScxmlExecutableContent::StringId theLocation)
- : name(theName)
- , expr(theExpr)
- , location(theLocation)
- {}
-
- Param()
- : name(QScxmlExecutableContent::NoString)
- , expr(QScxmlExecutableContent::NoInstruction)
- , location(QScxmlExecutableContent::NoString)
- {}
- };
-
QScxmlInvokableServiceFactory(QScxmlExecutableContent::StringId invokeLocation,
QScxmlExecutableContent::EvaluatorId srcexpr,
QScxmlExecutableContent::StringId id,
@@ -106,7 +85,7 @@ public:
QScxmlExecutableContent::StringId idlocation,
const QVector<QScxmlExecutableContent::StringId> &namelist,
bool autoforward,
- const QVector<Param> &params,
+ const QVector<QScxmlExecutableContent::Param> &params,
QScxmlExecutableContent::ContainerId finalizeContent);
virtual ~QScxmlInvokableServiceFactory();
@@ -153,7 +132,7 @@ public:
QScxmlExecutableContent::StringId idlocation,
const QVector<QScxmlExecutableContent::StringId> &namelist,
bool doAutoforward,
- const QVector<Param> &params,
+ const QVector<QScxmlExecutableContent::Param> &params,
QScxmlExecutableContent::ContainerId finalize);
protected:
@@ -166,6 +145,48 @@ protected:
QScxmlStateMachine *parent);
};
+template<class T>
+class QScxmlInvokeScxmlFactory: public QScxmlInvokableScxmlServiceFactory
+{
+public:
+ QScxmlInvokeScxmlFactory(QScxmlExecutableContent::StringId invokeLocation,
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId idPrefix,
+ QScxmlExecutableContent::StringId idlocation,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ bool doAutoforward,
+ const QVector<QScxmlExecutableContent::Param> &params,
+ QScxmlExecutableContent::ContainerId finalize)
+ : QScxmlInvokableScxmlServiceFactory(invokeLocation, QScxmlExecutableContent::NoInstruction,
+ id, idPrefix, idlocation, namelist, doAutoforward,
+ params, finalize)
+ {}
+
+ QScxmlInvokableService *invoke(QScxmlStateMachine *parent) Q_DECL_OVERRIDE
+ {
+ return finishInvoke(new T, parent);
+ }
+};
+
+class Q_SCXML_EXPORT QScxmlDynamicScxmlFactory: public QScxmlInvokableScxmlServiceFactory
+{
+public:
+ QScxmlDynamicScxmlFactory(QScxmlExecutableContent::StringId invokeLocation,
+ QScxmlExecutableContent::EvaluatorId srcexpr,
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId idPrefix,
+ QScxmlExecutableContent::StringId idlocation,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ bool doAutoforward,
+ const QVector<QScxmlExecutableContent::Param> &params,
+ QScxmlExecutableContent::ContainerId finalize)
+ : QScxmlInvokableScxmlServiceFactory(invokeLocation, srcexpr, id, idPrefix, idlocation,
+ namelist, doAutoforward, params, finalize)
+ {}
+
+ QScxmlInvokableService *invoke(QScxmlStateMachine *parent) Q_DECL_OVERRIDE;
+};
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QScxmlInvokableService *)
diff --git a/src/scxml/qscxmlparser.cpp b/src/scxml/qscxmlparser.cpp
index 3bb9720..057a04f 100644
--- a/src/scxml/qscxmlparser.cpp
+++ b/src/scxml/qscxmlparser.cpp
@@ -51,10 +51,11 @@
#ifndef BUILD_QSCXMLC
#include "qscxmlnulldatamodel.h"
#include "qscxmlecmascriptdatamodel.h"
-#include "qscxmlqstates.h"
+#include "qscxmlinvokableservice.h"
#include "qscxmldatamodel_p.h"
#include "qscxmlstatemachine_p.h"
#include "qscxmlstatemachine.h"
+#include "qscxmltabledata_p.h"
#include <QState>
#include <QHistoryState>
@@ -116,7 +117,6 @@ public:
private:
bool visit(DocumentModel::Scxml *scxml) Q_DECL_OVERRIDE
{
- Q_ASSERT(scxml->initialStates.isEmpty());
if (!scxml->name.isEmpty() && !isValidToken(scxml->name, XmlNmtoken)) {
error(scxml->xmlLocation,
QStringLiteral("scxml name '%1' is not a valid XML Nmtoken").arg(scxml->name));
@@ -124,15 +124,17 @@ private:
if (scxml->initial.isEmpty()) {
if (auto firstChild = firstAbstractState(scxml)) {
- scxml->initialStates.append(firstChild);
+ scxml->initialTransition = createInitialTransition({firstChild});
}
} else {
+ QVector<DocumentModel::AbstractState *> initialStates;
foreach (const QString &initial, scxml->initial) {
if (DocumentModel::AbstractState *s = m_stateById.value(initial))
- scxml->initialStates.append(s);
+ initialStates.append(s);
else
error(scxml->xmlLocation, QStringLiteral("initial state '%1' not found for <scxml> element").arg(initial));
}
+ scxml->initialTransition = createInitialTransition(initialStates);
}
m_parentNodes.append(scxml);
@@ -147,28 +149,38 @@ private:
bool visit(DocumentModel::State *state) Q_DECL_OVERRIDE
{
- Q_ASSERT(state->initialStates.isEmpty());
-
if (!state->id.isEmpty() && !isValidToken(state->id, XmlNCName)) {
error(state->xmlLocation, QStringLiteral("'%1' is not a valid XML ID").arg(state->id));
- } else if (state->type != DocumentModel::State::Initial) {
+ } else {
validateStateName(state->xmlLocation, state->id);
}
- if (state->initial.isEmpty()) {
- if (auto firstChild = firstAbstractState(state)) {
- state->initialStates += firstChild;
+ if (state->initialTransition == nullptr) {
+ if (state->initial.isEmpty()) {
+ if (auto firstChild = firstAbstractState(state)) {
+ state->initialTransition = createInitialTransition({firstChild});
+ }
+ } else {
+ Q_ASSERT(state->type == DocumentModel::State::Normal);
+ QVector<DocumentModel::AbstractState *> initialStates;
+ foreach (const QString &initialState, state->initial) {
+ if (DocumentModel::AbstractState *s = m_stateById.value(initialState)) {
+ initialStates.append(s);
+ } else {
+ error(state->xmlLocation,
+ QStringLiteral("undefined initial state '%1' for state '%2'")
+ .arg(initialState, state->id));
+ }
+ }
+ state->initialTransition = createInitialTransition(initialStates);
}
} else {
- Q_ASSERT(state->type == DocumentModel::State::Normal);
- foreach (const QString &initialState, state->initial) {
- if (DocumentModel::AbstractState *s = m_stateById.value(initialState)) {
- state->initialStates += s;
- } else {
- error(state->xmlLocation,
- QStringLiteral("undefined initial state '%1' for state '%2'")
- .arg(initialState, state->id));
- }
+ if (state->initial.isEmpty()) {
+ visit(state->initialTransition);
+ } else {
+ error(state->xmlLocation,
+ QStringLiteral("initial transition and initial attribute for state '%1'")
+ .arg(state->id));
}
}
@@ -177,27 +189,8 @@ private:
break;
case DocumentModel::State::Parallel:
if (!state->initial.isEmpty()) {
- error(state->xmlLocation, QStringLiteral("parallel states cannot have an initial state"));
- }
- break;
- case DocumentModel::State::Initial:
- if (transitionCount(state) != 1)
- error(state->xmlLocation, QStringLiteral("an initial state can only have one transition, but has '%1'").arg(transitionCount(state)));
- if (DocumentModel::Transition *t = firstTransition(state)) {
- if (!t->events.isEmpty() || !t->condition.isNull()) {
- error(t->xmlLocation, QStringLiteral("the transition in an initial state cannot have an event or a condition"));
- }
- if (t->targets.isEmpty()) {
- error(t->xmlLocation, QStringLiteral("the transition in an initial state must have at least one target"));
- }
- }
- foreach (DocumentModel::StateOrTransition *child, state->children) {
- if (DocumentModel::State *s = child->asState()) {
- error(s->xmlLocation, QStringLiteral("substates are not allowed in initial states"));
- }
- }
- if (parentState() == Q_NULLPTR) {
- error(state->xmlLocation, QStringLiteral("initial states can only occur in a state"));
+ error(state->xmlLocation,
+ QStringLiteral("parallel states cannot have an initial state"));
}
break;
case DocumentModel::State::Final:
@@ -296,6 +289,9 @@ private:
bool visit(DocumentModel::Invoke *node) Q_DECL_OVERRIDE
{
+ if (!node->srcexpr.isEmpty())
+ return false;
+
if (node->content.isNull()) {
error(node->xmlLocation, QStringLiteral("no valid content found in <invoke> tag"));
} else {
@@ -446,6 +442,18 @@ private:
return Q_NULLPTR;
}
+ DocumentModel::Transition *createInitialTransition(
+ const QVector<DocumentModel::AbstractState *> &states)
+ {
+ auto *newTransition = m_doc->newTransition(nullptr, DocumentModel::XmlLocation(-1, -1));
+ foreach (auto *s, states) {
+ newTransition->targets.append(s->id);
+ }
+
+ newTransition->targetStates = states;
+ return newTransition;
+ }
+
void checkExpr(const DocumentModel::XmlLocation &loc, const QString &tag, const QString &attrName, const QString &attrValue)
{
if (m_doc->root->dataModel == DocumentModel::Scxml::NullDataModel && !attrValue.isEmpty()) {
@@ -735,8 +743,32 @@ private:
};
#ifndef BUILD_QSCXMLC
-class QStateMachineBuilder;
-class DynamicStateMachine: public QScxmlStateMachine, public QScxmlEventFilter
+class InvokeDynamicScxmlFactory: public QScxmlInvokableScxmlServiceFactory
+{
+public:
+ InvokeDynamicScxmlFactory(QScxmlExecutableContent::StringId invokeLocation,
+ QScxmlExecutableContent::EvaluatorId srcexpr,
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId idPrefix,
+ QScxmlExecutableContent::StringId idlocation,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ bool autoforward,
+ const QVector<QScxmlExecutableContent::Param> &params,
+ QScxmlExecutableContent::ContainerId finalize)
+ : QScxmlInvokableScxmlServiceFactory(invokeLocation, srcexpr, id, idPrefix, idlocation,
+ namelist, autoforward, params, finalize)
+ {}
+
+ void setContent(const QSharedPointer<DocumentModel::ScxmlDocument> &content)
+ { m_content = content; }
+
+ QScxmlInvokableService *invoke(QScxmlStateMachine *child) Q_DECL_OVERRIDE;
+
+private:
+ QSharedPointer<DocumentModel::ScxmlDocument> m_content;
+};
+
+class DynamicStateMachine: public QScxmlStateMachine, public QScxmlInternal::GeneratedTableData
{
// Manually expanded from Q_OBJECT macro:
public:
@@ -764,35 +796,30 @@ public:
}
private:
- static void qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+ Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *_o, QMetaObject::Call _c,
+ int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
DynamicStateMachine *_t = static_cast<DynamicStateMachine *>(_o);
- if (_id >= _t->m_eventNamesByIndex.size() || _id < 0) {
+ if (_id < 0) {
// out of bounds
return;
}
- if (_id >= _t->m_firstSubStateMachineSignal && _id < _t->m_firstSlot) {
+ if (_id < _t->m_firstSlot) {
// these signals are only emitted, not activated by another signal
return;
}
- if (_id >= _t->m_firstStateChangedSignal && _id < _t->m_firstSubStateMachineSignal) {
- // re-propagate QAbstractState::activeChanged as stateChanged
- QMetaObject::activate(_t, _t->m_metaObject, _id, _a);
- return;
- }
// We have 1 kind of slots: those to submit events.
- const QString &event = _t->m_eventNamesByIndex.at(_id);
- if (!event.isEmpty()) {
- if (_id < _t->m_firstSlotWithoutData) {
- QVariant data = *reinterpret_cast< QVariant(*)>(_a[1]);
- if (data.canConvert<QJSValue>()) {
- data = data.value<QJSValue>().toVariant();
- }
- _t->submitEvent(event, data);
- } else {
- _t->submitEvent(event, QVariant());
+ if (_id < _t->m_firstSlotWithoutData) {
+ const QString &event = _t->m_incomingEvents.at(_id - _t->m_firstSlot);
+ QVariant data = *reinterpret_cast< QVariant(*)>(_a[1]);
+ if (data.canConvert<QJSValue>()) {
+ data = data.value<QJSValue>().toVariant();
}
+ _t->submitEvent(event, data);
+ } else {
+ const QString &event = _t->m_incomingEvents.at(_id - _t->m_firstSlotWithoutData);
+ _t->submitEvent(event, QVariant());
}
} else if (_c == QMetaObject::RegisterPropertyMetaType) {
DynamicStateMachine *_t = static_cast<DynamicStateMachine *>(_o);
@@ -804,16 +831,14 @@ private:
} else if (_c == QMetaObject::ReadProperty) {
DynamicStateMachine *_t = static_cast<DynamicStateMachine *>(_o);
void *_v = _a[0];
- if (_id >= 0 && _id < _t->m_propertyNamesByIndex.size()) {
+ if (_id >= 0 && _id < _t->m_propertyCount) {
if (_id < _t->m_firstSubStateMachineProperty) {
// getter for the state
- auto smp = QScxmlStateMachinePrivate::get(_t);
- auto name = _t->m_propertyNamesByIndex.at(_id);
- *reinterpret_cast<bool*>(_v) = smp->stateByScxmlName(name)->active();
+ *reinterpret_cast<bool*>(_v) = _t->isActive(_id);
} else {
// getter for a child statemachine
int idx = _id - _t->m_firstSubStateMachineProperty;
- *reinterpret_cast<QScxmlStateMachine **>(_v) = _t->m_subStateMachines.at(idx);
+ *reinterpret_cast<QScxmlStateMachine **>(_v) = _t->subStateMachine(idx);
}
}
}
@@ -821,34 +846,31 @@ private:
// end of Q_OBJECT macro
private:
- friend QStateMachineBuilder;
DynamicStateMachine()
: m_metaObject(Q_NULLPTR)
+ , m_propertyCount(0)
, m_firstSubStateMachineSignal(0)
, m_firstSlot(0)
, m_firstSlotWithoutData(0)
, m_firstSubStateMachineProperty(0)
{
- // Temporarily wire up the QMetaObject, because qobject_cast needs it while building MyQStateMachine.
+ // Temporarily wire up the QMetaObject
QMetaObjectBuilder b;
b.setClassName("DynamicStateMachine");
b.setSuperClass(&QScxmlStateMachine::staticMetaObject);
b.setStaticMetacallFunction(qt_static_metacall);
m_metaObject = b.toMetaObject();
-
- setScxmlEventFilter(this);
}
- void initDynamicParts(const QSet<QString> &eventSignals,
- const QSet<QString> &eventSlots,
- const QList<QString> &stateNames,
- const QList<QString> &subStateMachineNames)
+ void initDynamicParts(const MetaDataInfo &info)
{
// Release the temporary QMetaObject.
Q_ASSERT(m_metaObject);
free(m_metaObject);
- m_eventNamesByIndex.reserve(eventSignals.size() + subStateMachineNames.size() + eventSlots.size());
+ m_incomingEvents = info.incomingEvents;
+ m_outgoingEvents = info.outgoingEvents;
+ std::sort(m_outgoingEvents.begin(), m_outgoingEvents.end());
// Build the real one.
QMetaObjectBuilder b;
@@ -857,77 +879,59 @@ private:
b.setStaticMetacallFunction(qt_static_metacall);
// signals
- foreach (const QString &eventName, eventSignals) {
- QByteArray signalName = eventName.toUtf8() + "(const QVariant &)";
- QMetaMethodBuilder signalBuilder = b.addSignal(signalName);
- signalBuilder.setParameterNames(init("data"));
- int idx = signalBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- m_eventNamesByIndex[idx] = eventName;
- }
-
- m_firstStateChangedSignal = m_eventNamesByIndex.size();
- foreach (const QString &stateName, stateNames) {
+ foreach (const QString &stateName, info.stateNames) {
auto name = stateName.toUtf8();
- QByteArray signalName = name + "Changed(bool)";
+ const QByteArray signalName = name + "Changed(bool)";
QMetaMethodBuilder signalBuilder = b.addSignal(signalName);
signalBuilder.setParameterNames(init("active"));
- int idx = signalBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
}
- m_firstSubStateMachineSignal = m_eventNamesByIndex.size();
- foreach (const QString &machineName, subStateMachineNames) {
+ m_firstSubStateMachineSignal = info.stateNames.size();
+ foreach (const QString &machineName, info.subStateMachineNames) {
auto name = machineName.toUtf8();
- QByteArray signalName = name + "Changed(QScxmlStateMachine *)";
+ const 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()));
+ }
+
+ foreach (const QString &eventName, info.outgoingEvents) {
+ const QByteArray signalName = eventName.toUtf8() + "(const QVariant &)";
+ QMetaMethodBuilder signalBuilder = b.addSignal(signalName);
+ signalBuilder.setParameterNames(init("data"));
}
// slots
- m_firstSlot = m_eventNamesByIndex.size();
- foreach (const QString &eventName, eventSlots) {
- QByteArray slotName = eventName.toUtf8() + "(const QVariant &)";
+ m_firstSlot = info.stateNames.size() + info.subStateMachineNames.size()
+ + info.outgoingEvents.size();
+ foreach (const QString &eventName, info.incomingEvents) {
+ const QByteArray slotName = eventName.toUtf8() + "(const QVariant &)";
QMetaMethodBuilder slotBuilder = b.addSlot(slotName);
slotBuilder.setParameterNames(init("data"));
- int idx = slotBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- m_eventNamesByIndex[idx] = eventName;
}
- m_firstSlotWithoutData = m_eventNamesByIndex.size();
- foreach (const QString &eventName, eventSlots) {
- QByteArray slotName = eventName.toUtf8() + "()";
- QMetaMethodBuilder slotBuilder = b.addSlot(slotName);
- int idx = slotBuilder.index();
- m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size()));
- m_eventNamesByIndex[idx] = eventName;
+ m_firstSlotWithoutData = m_firstSlot + info.incomingEvents.size();
+ foreach (const QString &eventName, info.incomingEvents) {
+ const QByteArray slotName = eventName.toUtf8() + "()";
+ b.addSlot(slotName);
}
// properties
- int stateNotifier = m_firstStateChangedSignal;
- foreach (const QString &stateName, stateNames) {
- QMetaPropertyBuilder prop = b.addProperty(stateName.toUtf8(), "bool", stateNotifier);
+ int notifier = 0;
+ foreach (const QString &stateName, info.stateNames) {
+ QMetaPropertyBuilder prop = b.addProperty(stateName.toUtf8(), "bool", notifier);
prop.setWritable(false);
- int idx = prop.index();
- m_propertyNamesByIndex.resize(std::max(idx + 1, m_propertyNamesByIndex.size()));
- m_propertyNamesByIndex[idx] = stateName;
- ++stateNotifier;
+ ++m_propertyCount;
+ ++notifier;
}
- m_firstSubStateMachineProperty = m_propertyNamesByIndex.size();
- int notifier = m_firstSubStateMachineSignal;
- foreach (const QString &machineName, subStateMachineNames) {
- QMetaPropertyBuilder prop = b.addProperty(machineName.toUtf8(), "QScxmlStateMachine *", notifier);
+ m_firstSubStateMachineProperty = m_propertyCount;
+ foreach (const QString &machineName, info.subStateMachineNames) {
+ QMetaPropertyBuilder prop = b.addProperty(machineName.toUtf8(), "QScxmlStateMachine *",
+ notifier);
prop.setWritable(false);
- int idx = prop.index();
- m_propertyNamesByIndex.resize(std::max(idx + 1, m_propertyNamesByIndex.size()));
- m_propertyNamesByIndex[idx] = machineName;
+ ++m_propertyCount;
++notifier;
}
- m_subStateMachines.resize(subStateMachineNames.size());
// And we're done
m_metaObject = b.toMetaObject();
@@ -935,48 +939,59 @@ private:
public:
~DynamicStateMachine()
- { if (m_metaObject) free(m_metaObject); }
-
- bool handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine) Q_DECL_OVERRIDE {
- Q_UNUSED(stateMachine);
-
- if (event->originType() != QStringLiteral("qt:signal")) {
- return true;
+ {
+ if (m_metaObject) {
+ free(m_metaObject);
}
+ }
- auto eventName = event->name();
- for (int i = 0; i < m_firstSubStateMachineSignal; ++i) {
- if (m_eventNamesByIndex.at(i) == eventName) {
- QVariant data = event->data();
- void *argv[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&data)) };
- QMetaObject::activate(this, metaObject(), i, argv);
- return false;
- }
- }
+ QScxmlInvokableServiceFactory *serviceFactory(int id) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return m_allFactoriesById.at(id); }
- return true;
+ int signalIndexForEvent(const QString &event) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto it = std::lower_bound(m_outgoingEvents.begin(), m_outgoingEvents.end(), event);
+ if (it != m_outgoingEvents.end() && *it == event) {
+ return int(std::distance(m_outgoingEvents.begin(), it));
+ } else {
+ return -1;
+ }
}
-protected:
- void setService(const QString &id, QScxmlInvokableService *service) Q_DECL_OVERRIDE
+ static DynamicStateMachine *build(DocumentModel::ScxmlDocument *doc)
{
- int idx = -1;
- for (int i = m_firstSubStateMachineProperty, ei = m_propertyNamesByIndex.size(); i != ei; ++i) {
- if (m_propertyNamesByIndex.at(i) == id) {
- idx = i - m_firstSubStateMachineProperty;
- break;
- }
- }
- if (idx < 0)
- return;
- auto scxml = service ? dynamic_cast<QScxmlInvokableScxml *>(service) : Q_NULLPTR;
- auto machine = scxml ? scxml->stateMachine() : Q_NULLPTR;
- if (m_subStateMachines.at(idx) != machine) {
- m_subStateMachines[idx] = machine;
- // emit changed signal:
- void *argv[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&machine)) };
- QMetaObject::activate(this, metaObject(), m_firstSubStateMachineSignal + idx, argv);
- }
+ auto stateMachine = new DynamicStateMachine;
+ MetaDataInfo info;
+ DataModelInfo dm;
+ auto factoryIdCreator = [stateMachine](QScxmlExecutableContent::StringId invokeLocation,
+ QScxmlExecutableContent::EvaluatorId srcexpr,
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId idPrefix,
+ QScxmlExecutableContent::StringId idlocation,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ bool autoforward,
+ const QVector<QScxmlExecutableContent::Param> &params,
+ QScxmlExecutableContent::ContainerId finalize,
+ const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int {
+ auto factory = new InvokeDynamicScxmlFactory(invokeLocation,
+ srcexpr,
+ id,
+ idPrefix,
+ idlocation,
+ namelist,
+ autoforward,
+ params,
+ finalize);
+ factory->setContent(content);
+ stateMachine->m_allFactoriesById.append(factory);
+ return stateMachine->m_allFactoriesById.size() - 1;
+ };
+
+ GeneratedTableData::build(doc, stateMachine, &info, &dm, factoryIdCreator);
+ stateMachine->setTableData(stateMachine);
+ stateMachine->initDynamicParts(info);
+
+ return stateMachine;
}
private:
@@ -990,389 +1005,17 @@ private:
}
private:
+ QVector<QScxmlInvokableServiceFactory *> m_allFactoriesById;
QMetaObject *m_metaObject;
- QVector<QString> m_eventNamesByIndex;
- QVector<QString> m_propertyNamesByIndex;
- QVector<QScxmlStateMachine *> m_subStateMachines;
+ QStringList m_incomingEvents;
+ QStringList m_outgoingEvents;
+ int m_propertyCount;
int m_firstSubStateMachineSignal;
- int m_firstStateChangedSignal;
int m_firstSlot;
int m_firstSlotWithoutData;
int m_firstSubStateMachineProperty;
};
-class InvokeDynamicScxmlFactory: public QScxmlInvokableScxmlServiceFactory
-{
-public:
- InvokeDynamicScxmlFactory(QScxmlExecutableContent::StringId invokeLocation,
- QScxmlExecutableContent::EvaluatorId srcexpr,
- QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix,
- QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- bool autoforward,
- const QVector<Param> &params,
- QScxmlExecutableContent::ContainerId finalize)
- : QScxmlInvokableScxmlServiceFactory(invokeLocation,
- srcexpr,
- id,
- idPrefix,
- idlocation,
- namelist,
- autoforward,
- params,
- finalize)
- {}
-
- void setContent(const QSharedPointer<DocumentModel::ScxmlDocument> &content)
- { m_content = content; }
-
- QScxmlInvokableService *invoke(QScxmlStateMachine *child) Q_DECL_OVERRIDE;
-
-private:
- QSharedPointer<DocumentModel::ScxmlDocument> m_content;
-};
-
-class QStateMachineBuilder: public QScxmlExecutableContent::Builder
-{
-public:
- QStateMachineBuilder()
- : m_stateMachine(Q_NULLPTR)
- , m_currentTransition(Q_NULLPTR)
- , m_bindLate(false)
- , m_qtMode(false)
- {}
-
- QScxmlStateMachine *build(DocumentModel::ScxmlDocument *doc)
- {
- m_stateMachine = Q_NULLPTR;
- m_parents.reserve(32);
- m_allTransitions.reserve(doc->allTransitions.size());
- m_docStatesToQStates.reserve(doc->allStates.size());
- m_qtMode = doc->qtMode;
-
- doc->root->accept(this);
- wireTransitions();
- applyInitialStates();
-
- QScxmlExecutableContent::DynamicTableData *td = tableData();
- td->setParent(m_stateMachine);
- m_stateMachine->setTableData(td);
- m_stateMachine->initDynamicParts(m_eventSignals, m_eventSlots, m_stateNames.keys(), m_subStateMachineNames.toList());
-
- const auto signalCode = QByteArray::number(QSIGNAL_CODE);
- for (auto it = m_stateNames.constBegin(), eit = m_stateNames.constEnd(); it != eit; ++it) {
- QByteArray signal = signalCode + it.key().toUtf8() + "Changed(bool)";
- QObject::connect(it.value(), SIGNAL(activeChanged(bool)), m_stateMachine, signal.constData());
- }
-
- m_parents.clear();
- m_allTransitions.clear();
- m_docStatesToQStates.clear();
- m_currentTransition = Q_NULLPTR;
-
- return m_stateMachine;
- }
-
-private:
- using NodeVisitor::visit;
- using QScxmlExecutableContent::Builder::createContext;
-
- bool visit(DocumentModel::Scxml *node) Q_DECL_OVERRIDE
- {
- m_stateMachine = new DynamicStateMachine;
-
- switch (node->binding) {
- case DocumentModel::Scxml::EarlyBinding:
- m_stateMachine->setDataBinding(QScxmlStateMachine::EarlyBinding);
- break;
- case DocumentModel::Scxml::LateBinding:
- m_stateMachine->setDataBinding(QScxmlStateMachine::LateBinding);
- m_bindLate = true;
- break;
- default:
- Q_UNREACHABLE();
- }
-
- setName(node->name);
-
- m_parents.append(QScxmlStateMachinePrivate::get(m_stateMachine)->m_qStateMachine);
- visit(node->children);
-
- m_dataElements.append(node->dataElements);
- if (node->script || !m_dataElements.isEmpty() || !node->initialSetup.isEmpty()) {
- setInitialSetup(startNewSequence());
- generate(m_dataElements);
- if (node->script) {
- node->script->accept(this);
- }
- visit(&node->initialSetup);
- endSequence();
- }
-
- m_parents.removeLast();
-
- foreach (auto initialState, node->initialStates) {
- Q_ASSERT(initialState);
- m_initialStates.append(qMakePair(QScxmlStateMachinePrivate::get(m_stateMachine)->m_qStateMachine, initialState));
- }
-
- return false;
- }
-
- bool visit(DocumentModel::State *node) Q_DECL_OVERRIDE
- {
- QAbstractState *newState = Q_NULLPTR;
- switch (node->type) {
- case DocumentModel::State::Normal: {
- auto s = new QScxmlState(currentParent());
- newState = s;
- foreach (DocumentModel::AbstractState *initialState, node->initialStates) {
- m_initialStates.append(qMakePair(s, initialState));
- }
- } break;
- case DocumentModel::State::Parallel: {
- auto s = new QScxmlState(currentParent());
- s->setChildMode(QState::ParallelStates);
- newState = s;
- } break;
- case DocumentModel::State::Initial: {
- auto s = new QScxmlState(currentParent());
- currentParent()->setInitialState(s);
- newState = s;
- } break;
- case DocumentModel::State::Final: {
- auto s = new QScxmlFinalState(currentParent());
- newState = s;
- s->setDoneData(generate(node->doneData));
- } break;
- default:
- Q_UNREACHABLE();
- }
-
- newState->setObjectName(node->id);
- m_stateNames.insert(node->id, newState);
-
- m_docStatesToQStates.insert(node, newState);
- m_parents.append(newState);
-
- if (!node->dataElements.isEmpty()) {
- if (m_bindLate) {
- qobject_cast<QScxmlState *>(newState)->setInitInstructions(startNewSequence());
- generate(node->dataElements);
- endSequence();
- } else {
- m_dataElements.append(node->dataElements);
- }
- }
-
- QScxmlExecutableContent::ContainerId onEntry = generate(node->onEntry);
- QScxmlExecutableContent::ContainerId onExit = generate(node->onExit);
- if (QScxmlState *s = qobject_cast<QScxmlState *>(newState)) {
- s->setOnEntryInstructions(onEntry);
- s->setOnExitInstructions(onExit);
- if (!node->invokes.isEmpty()) {
- QVector<QScxmlInvokableServiceFactory *> factories;
- foreach (DocumentModel::Invoke *invoke, node->invokes) {
- auto ctxt = createContext(QStringLiteral("invoke"));
- QVector<QScxmlExecutableContent::StringId> namelist;
- foreach (const QString &name, invoke->namelist)
- namelist += addString(name);
- QVector<QScxmlInvokableServiceFactory::Param> params;
- foreach (DocumentModel::Param *param, invoke->params) {
- QScxmlInvokableServiceFactory::Param p;
- p.name = addString(param->name);
- p.expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"), param->expr);
- p.location = addString(param->location);
- params.append(p);
- }
- QScxmlExecutableContent::ContainerId finalize = QScxmlExecutableContent::NoInstruction;
- if (!invoke->finalize.isEmpty()) {
- finalize = startNewSequence();
- visit(&invoke->finalize);
- endSequence();
- }
- auto factory = new InvokeDynamicScxmlFactory(ctxt,
- createEvaluatorString(QStringLiteral("invoke"),
- QStringLiteral("srcexpr"),
- invoke->srcexpr),
- addString(invoke->id),
- addString(node->id + QStringLiteral(".session-")),
- addString(invoke->idLocation),
- namelist,
- invoke->autoforward,
- params,
- finalize);
- factory->setContent(invoke->content);
- factories.append(factory);
- QString name = invoke->content->root->name;
- if (!name.isEmpty()) {
- m_subStateMachineNames.insert(name);
- }
- }
- s->setInvokableServiceFactories(factories);
- }
- } else if (QScxmlFinalState *f = qobject_cast<QScxmlFinalState *>(newState)) {
- f->setOnEntryInstructions(onEntry);
- f->setOnExitInstructions(onExit);
- } else {
- Q_UNREACHABLE();
- }
-
- visit(node->children);
-
- m_parents.removeLast();
- return false;
- }
-
- bool visit(DocumentModel::Transition *node) Q_DECL_OVERRIDE
- {
- if (m_qtMode) {
- m_eventSlots.unite(node->events.toSet());
- }
-
- auto newTransition = new QScxmlTransition(node->events);
- if (QHistoryState *parent = qobject_cast<QHistoryState*>(m_parents.last())) {
- parent->setDefaultTransition(newTransition);
- } else {
- currentParent()->addTransition(newTransition);
- }
-
- if (node->condition) {
- auto cond = createEvaluatorBool(QStringLiteral("transition"), QStringLiteral("cond"), *node->condition.data());
- newTransition->setConditionalExpression(cond);
- }
-
- switch (node->type) {
- case DocumentModel::Transition::External:
- newTransition->setTransitionType(QAbstractTransition::ExternalTransition);
- break;
- case DocumentModel::Transition::Internal:
- newTransition->setTransitionType(QAbstractTransition::InternalTransition);
- break;
- default:
- Q_UNREACHABLE();
- }
-
- m_allTransitions.insert(newTransition, node);
- if (!node->instructionsOnTransition.isEmpty()) {
- m_currentTransition = newTransition;
- newTransition->setInstructionsOnTransition(startNewSequence());
- visit(&node->instructionsOnTransition);
- endSequence();
- m_currentTransition = 0;
- }
- Q_ASSERT(newTransition->stateMachine());
- return false;
- }
-
- bool visit(DocumentModel::HistoryState *state) Q_DECL_OVERRIDE
- {
- QHistoryState *newState = new QScxmlHistoryState(currentParent());
- switch (state->type) {
- case DocumentModel::HistoryState::Shallow:
- newState->setHistoryType(QHistoryState::ShallowHistory);
- break;
- case DocumentModel::HistoryState::Deep:
- newState->setHistoryType(QHistoryState::DeepHistory);
- break;
- default:
- Q_UNREACHABLE();
- }
-
- newState->setObjectName(state->id);
- m_docStatesToQStates.insert(state, newState);
- m_parents.append(newState);
- return true;
- }
-
- void endVisit(DocumentModel::HistoryState *) Q_DECL_OVERRIDE
- {
- m_parents.removeLast();
- }
-
- bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE
- {
- if (m_qtMode && node->type == QStringLiteral("qt:signal")) {
- m_eventSignals.insert(node->event);
- }
-
- return QScxmlExecutableContent::Builder::visit(node);
- }
-
-private: // Utility methods
- QState *currentParent() const
- {
- if (m_parents.isEmpty())
- return Q_NULLPTR;
-
- QState *parent = qobject_cast<QState*>(m_parents.last());
- Q_ASSERT(parent);
- return parent;
- }
-
- void wireTransitions()
- {
- for (QHash<QAbstractTransition *, DocumentModel::Transition*>::const_iterator i = m_allTransitions.begin(), ei = m_allTransitions.end(); i != ei; ++i) {
- QList<QAbstractState *> targets;
- targets.reserve(i.value()->targets.size());
- foreach (DocumentModel::AbstractState *targetState, i.value()->targetStates) {
- QAbstractState *target = m_docStatesToQStates.value(targetState);
- Q_ASSERT(target);
- targets.append(target);
- }
- i.key()->setTargetStates(targets);
-
- if (DebugHelper_NameTransitions)
- i.key()->setObjectName(QStringLiteral("%1 -> %2").arg(i.key()->parent()->objectName(), i.value()->targets.join(QStringLiteral(","))));
- }
- }
-
- void applyInitialStates()
- {
- foreach (const auto &init, m_initialStates) {
- Q_ASSERT(init.second);
- auto initialState = m_docStatesToQStates.value(init.second);
- Q_ASSERT(initialState);
- init.first->setInitialState(initialState);
- }
- }
-
- QString createContextString(const QString &instrName) const Q_DECL_OVERRIDE
- {
- if (m_currentTransition) {
- QString state;
- if (QState *s = m_currentTransition->sourceState()) {
- state = QStringLiteral(" of state '%1'").arg(s->objectName());
- }
- return QStringLiteral("%1 instruction in transition %2 %3").arg(instrName, m_currentTransition->objectName(), state);
- } else {
- return QStringLiteral("%1 instruction in state %2").arg(instrName, m_parents.last()->objectName());
- }
- }
-
- QString createContext(const QString &instrName, const QString &attrName, const QString &attrValue) const Q_DECL_OVERRIDE
- {
- QString location = createContextString(instrName);
- return QStringLiteral("%1 with %2=\"%3\"").arg(location, attrName, attrValue);
- }
-
-private:
- DynamicStateMachine *m_stateMachine;
- QVector<QAbstractState *> m_parents;
- QHash<QAbstractTransition *, DocumentModel::Transition*> m_allTransitions;
- QHash<DocumentModel::AbstractState *, QAbstractState *> m_docStatesToQStates;
- QAbstractTransition *m_currentTransition;
- QVector<QPair<QState *, DocumentModel::AbstractState *>> m_initialStates;
- bool m_bindLate;
- bool m_qtMode;
- QVector<DocumentModel::DataElement *> m_dataElements;
- QSet<QString> m_eventSignals;
- QSet<QString> m_eventSlots;
- QHash<QString, QAbstractState *> m_stateNames;
- QSet<QString> m_subStateMachineNames;
-};
-
inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke(QScxmlStateMachine *parent)
{
bool ok = true;
@@ -1383,7 +1026,7 @@ inline QScxmlInvokableService *InvokeDynamicScxmlFactory::invoke(QScxmlStateMach
if (!srcexpr.isEmpty())
return loadAndInvokeDynamically(parent, srcexpr);
- auto child = QStateMachineBuilder().build(m_content.data());
+ auto child = DynamicStateMachine::build(m_content.data());
auto dm = QScxmlDataModelPrivate::instantiateDataModel(m_content->root->dataModel);
dm->setParent(child);
@@ -1401,9 +1044,9 @@ QScxmlInvokableService *QScxmlInvokableScxmlServiceFactory::loadAndInvokeDynamic
{
QScxmlParser::Loader *loader = parent->loader();
+ const QString baseDir = sourceUrl.isEmpty() ? QString() : QFileInfo(sourceUrl).path();
QStringList errs;
- const QByteArray data = loader->load(sourceUrl, sourceUrl.isEmpty() ?
- QString() : QFileInfo(sourceUrl).path(), &errs);
+ const QByteArray data = loader->load(sourceUrl, baseDir, &errs);
if (!errs.isEmpty()) {
qWarning() << errs;
@@ -1429,7 +1072,7 @@ QScxmlInvokableService *QScxmlInvokableScxmlServiceFactory::loadAndInvokeDynamic
return Q_NULLPTR;
}
- auto child = QStateMachineBuilder().build(mainDoc);
+ auto child = DynamicStateMachine::build(mainDoc);
auto dm = QScxmlDataModelPrivate::instantiateDataModel(mainDoc->root->dataModel);
dm->setParent(child);
@@ -1564,7 +1207,7 @@ QScxmlStateMachine *QScxmlParser::instantiateStateMachine() const
#else // BUILD_QSCXMLC
DocumentModel::ScxmlDocument *doc = d->scxmlDocument();
if (doc && doc->root) {
- return QStateMachineBuilder().build(doc);
+ return DynamicStateMachine::build(doc);
} else {
class InvalidStateMachine: public QScxmlStateMachine {
public:
@@ -2286,15 +1929,36 @@ bool QScxmlParserPrivate::preReadElementInitial()
addError(QStringLiteral("Explicit initial state for parallel states not supported (only implicitly through the initial states of its substates)"));
return false;
}
- auto newState = m_doc->newState(m_currentState, DocumentModel::State::Initial, xmlLocation());
- m_currentState = newState;
return true;
}
bool QScxmlParserPrivate::preReadElementTransition()
{
+ // Parser stack at this point:
+ // <transition>
+ // <initial>
+ // <state> or <scxml>
+ //
+ // Or:
+ // <transition>
+ // <state> or <scxml>
+
+ DocumentModel::Transition *transition = nullptr;
+ if (previous().kind == ParserState::Initial) {
+ transition = m_doc->newTransition(nullptr, xmlLocation());
+ const auto &initialParentState = m_stack.at(m_stack.size() - 3);
+ if (initialParentState.kind == ParserState::Scxml) {
+ m_currentState->asScxml()->initialTransition = transition;
+ } else if (initialParentState.kind == ParserState::State) {
+ m_currentState->asState()->initialTransition = transition;
+ } else {
+ Q_UNREACHABLE();
+ }
+ } else {
+ transition = m_doc->newTransition(m_currentState, xmlLocation());
+ }
+
const QXmlStreamAttributes attributes = m_reader->attributes();
- auto transition = m_doc->newTransition(m_currentState, xmlLocation());
transition->events = attributes.value(QLatin1String("event")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
transition->targets = attributes.value(QLatin1String("target")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
if (attributes.hasAttribute(QStringLiteral("cond")))
@@ -2664,7 +2328,6 @@ bool QScxmlParserPrivate::postReadElementParallel()
bool QScxmlParserPrivate::postReadElementInitial()
{
- currentStateUp();
return true;
}
@@ -2859,11 +2522,6 @@ bool QScxmlParserPrivate::postReadElementInvoke()
QXmlStreamReader reader(data);
parseSubDocument(i, &reader, fileName);
}
- } else { // invoke always expects sub document
- DocumentModel::ScxmlDocument *doc = new DocumentModel::ScxmlDocument(m_fileName);
- doc->root = new DocumentModel::Scxml(DocumentModel::XmlLocation(0, 0));
- i->content.reset(doc);
- m_doc->allSubDocuments.append(i->content.data());
}
} else if (!fileName.isEmpty()) {
addError(QStringLiteral("both src and content given to invoke"));
diff --git a/src/scxml/qscxmlparser_p.h b/src/scxml/qscxmlparser_p.h
index 42b61f8..6738f65 100644
--- a/src/scxml/qscxmlparser_p.h
+++ b/src/scxml/qscxmlparser_p.h
@@ -281,7 +281,7 @@ struct AbstractState: public StateContainer
struct State: public AbstractState, public StateOrTransition
{
- enum Type { Normal, Parallel, Initial, Final };
+ enum Type { Normal, Parallel, Final };
QStringList initial;
QVector<DataElement *> dataElements;
@@ -292,12 +292,13 @@ struct State: public AbstractState, public StateOrTransition
QVector<Invoke *> invokes;
Type type;
- QVector<AbstractState *> initialStates; // filled during verification
+ Transition *initialTransition; // when not set, it is filled during verification
State(const XmlLocation &xmlLocation)
: StateOrTransition(xmlLocation)
, doneData(Q_NULLPTR)
, type(Normal)
+ , initialTransition(Q_NULLPTR)
{}
void add(StateOrTransition *s) Q_DECL_OVERRIDE
@@ -320,7 +321,7 @@ struct Transition: public StateOrTransition
InstructionSequence instructionsOnTransition;
Type type;
- QVector<AbstractState *> targetStates; // filled during verification
+ QVector<AbstractState *> targetStates; // when not set, it is filled during verification
Transition(const XmlLocation &xmlLocation)
: StateOrTransition(xmlLocation)
@@ -379,7 +380,7 @@ struct Scxml: public StateContainer, public Node
QScopedPointer<Script> script;
InstructionSequence initialSetup;
- QVector<AbstractState *> initialStates; // filled during verification
+ Transition *initialTransition;
Scxml(const XmlLocation &xmlLocation)
: Node(xmlLocation)
@@ -449,7 +450,9 @@ struct ScxmlDocument
{
Transition *t = newNode<Transition>(xmlLocation);
allTransitions.append(t);
- parent->add(t);
+ if (parent != nullptr) {
+ parent->add(t);
+ }
return t;
}
diff --git a/src/scxml/qscxmlqstates.cpp b/src/scxml/qscxmlqstates.cpp
deleted file mode 100644
index d40ef20..0000000
--- a/src/scxml/qscxmlqstates.cpp
+++ /dev/null
@@ -1,410 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qscxmlglobals_p.h"
-#include "qscxmlqstates_p.h"
-#include "qscxmlstatemachine_p.h"
-
-#undef DUMP_EVENT
-#ifdef DUMP_EVENT
-#include <QJSEngine>
-#include "qscxmlecmascriptdatamodel.h"
-#endif
-
-QT_BEGIN_NAMESPACE
-
-static QStringList filterEmpty(const QStringList &events) {
- QStringList res;
- int oldI = 0;
- for (int i = 0; i < events.size(); ++i) {
- if (events.at(i).isEmpty()) {
- res.append(events.mid(oldI, i - oldI));
- oldI = i + 1;
- }
- }
- if (oldI > 0) {
- res.append(events.mid(oldI));
- return res;
- }
- return events;
-}
-
-QScxmlStatePrivate::QScxmlStatePrivate()
- : initInstructions(QScxmlExecutableContent::NoInstruction)
- , onEntryInstructions(QScxmlExecutableContent::NoInstruction)
- , onExitInstructions(QScxmlExecutableContent::NoInstruction)
-{}
-
-QScxmlStatePrivate::~QScxmlStatePrivate()
-{
- qDeleteAll(invokableServiceFactories);
-}
-
-QScxmlState::QScxmlState(QState *parent)
- : QState(*new QScxmlStatePrivate, parent)
-{}
-
-QScxmlState::QScxmlState(QScxmlStateMachine *parent)
- : QState(*new QScxmlStatePrivate, QScxmlStateMachinePrivate::get(parent)->m_qStateMachine)
-{}
-
-QScxmlState::~QScxmlState()
-{}
-
-void QScxmlState::setAsInitialStateFor(QScxmlState *state)
-{
- state->setInitialState(this);
-}
-
-void QScxmlState::setAsInitialStateFor(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->setInitialState(this);
-}
-
-QScxmlStateMachine *QScxmlState::stateMachine() const {
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-QString QScxmlState::stateLocation() const
-{
- return QStringLiteral("State %1").arg(objectName());
-}
-
-void QScxmlState::setInitInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlState);
- d->initInstructions = instructions;
-}
-
-void QScxmlState::setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlState);
- d->onEntryInstructions = instructions;
-}
-
-void QScxmlState::setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlState);
- d->onExitInstructions = instructions;
-}
-
-void QScxmlState::setInvokableServiceFactories(const QVector<QScxmlInvokableServiceFactory *> &factories)
-{
- Q_D(QScxmlState);
- d->invokableServiceFactories = factories;
-}
-
-void QScxmlState::onEntry(QEvent *event)
-{
- Q_D(QScxmlState);
-
- auto sp = QScxmlStateMachinePrivate::get(stateMachine());
- if (d->initInstructions != QScxmlExecutableContent::NoInstruction) {
- sp->m_executionEngine->execute(d->initInstructions);
- d->initInstructions = QScxmlExecutableContent::NoInstruction;
- }
- QState::onEntry(event);
- auto sm = stateMachine();
- QScxmlStateMachinePrivate::get(sm)->m_executionEngine->execute(d->onEntryInstructions);
- foreach (QScxmlInvokableServiceFactory *f, d->invokableServiceFactories) {
- if (auto service = f->invoke(stateMachine())) {
- d->invokedServices.append(service);
- d->servicesWaitingToStart.append(service);
- sp->addService(service);
- }
- }
- emit didEnter();
-}
-
-void QScxmlState::onExit(QEvent *event)
-{
- Q_D(QScxmlState);
-
- emit willExit();
- auto sm = stateMachine();
- QScxmlStateMachinePrivate::get(sm)->m_executionEngine->execute(d->onExitInstructions);
- QState::onExit(event);
-}
-
-QScxmlFinalStatePrivate::QScxmlFinalStatePrivate()
- : doneData(QScxmlExecutableContent::NoInstruction)
- , onEntryInstructions(QScxmlExecutableContent::NoInstruction)
- , onExitInstructions(QScxmlExecutableContent::NoInstruction)
-{}
-
-QScxmlFinalStatePrivate::~QScxmlFinalStatePrivate()
-{}
-
-QScxmlFinalState::QScxmlFinalState(QState *parent)
- : QFinalState(*new QScxmlFinalStatePrivate, parent)
-{}
-
-QScxmlFinalState::QScxmlFinalState(QScxmlStateMachine *parent)
- : QFinalState(*new QScxmlFinalStatePrivate, QScxmlStateMachinePrivate::get(parent)->m_qStateMachine)
-{}
-
-QScxmlFinalState::~QScxmlFinalState()
-{}
-
-void QScxmlFinalState::setAsInitialStateFor(QScxmlState *state)
-{
- state->setInitialState(this);
-}
-
-void QScxmlFinalState::setAsInitialStateFor(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->setInitialState(this);
-}
-
-QScxmlStateMachine *QScxmlFinalState::stateMachine() const {
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-QScxmlHistoryState::QScxmlHistoryState(QState *parent)
- : QHistoryState(parent)
-{
-}
-
-QScxmlHistoryState::~QScxmlHistoryState()
-{
-}
-
-void QScxmlHistoryState::setAsInitialStateFor(QScxmlState *state)
-{
- state->setInitialState(this);
-}
-
-void QScxmlHistoryState::setAsInitialStateFor(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->setInitialState(this);
-}
-
-QScxmlStateMachine *QScxmlHistoryState::stateMachine() const
-{
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-QScxmlExecutableContent::ContainerId QScxmlFinalState::doneData() const
-{
- Q_D(const QScxmlFinalState);
- return d->doneData;
-}
-
-void QScxmlFinalState::setDoneData(QScxmlExecutableContent::ContainerId doneData)
-{
- Q_D(QScxmlFinalState);
- d->doneData = doneData;
-}
-
-void QScxmlFinalState::setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlFinalState);
- d->onEntryInstructions = instructions;
-}
-
-void QScxmlFinalState::setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlFinalState);
- d->onExitInstructions = instructions;
-}
-
-void QScxmlFinalState::onEntry(QEvent *event)
-{
- Q_D(QScxmlFinalState);
-
- QFinalState::onEntry(event);
- auto smp = QScxmlStateMachinePrivate::get(stateMachine());
- smp->m_executionEngine->execute(d->onEntryInstructions);
-}
-
-void QScxmlFinalState::onExit(QEvent *event)
-{
- Q_D(QScxmlFinalState);
-
- QFinalState::onExit(event);
- QScxmlStateMachinePrivate::get(stateMachine())->m_executionEngine->execute(d->onExitInstructions);
-}
-
-QScxmlBaseTransition::QScxmlBaseTransition(QState *sourceState, const QStringList &eventSelector)
- : QAbstractTransition(*new QScxmlBaseTransitionPrivate, sourceState)
-{
- Q_D(QScxmlBaseTransition);
- d->eventSelector = eventSelector;
-}
-
-QScxmlBaseTransition::QScxmlBaseTransition(QScxmlBaseTransitionPrivate &dd, QState *parent,
- const QStringList &eventSelector)
- : QAbstractTransition(dd, parent)
-{
- Q_D(QScxmlBaseTransition);
- d->eventSelector = eventSelector;
-}
-
-QScxmlBaseTransition::~QScxmlBaseTransition()
-{}
-
-QScxmlStateMachine *QScxmlBaseTransition::stateMachine() const {
- if (QScxmlInternal::WrappedQStateMachine *t = qobject_cast<QScxmlInternal::WrappedQStateMachine *>(parent()))
- return t->stateMachine();
- if (QState *s = sourceState())
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(s->machine())->stateMachine();
- qCWarning(qscxmlLog) << "could not find Scxml::StateMachine in " << transitionLocation();
- return 0;
-}
-
-QString QScxmlBaseTransition::transitionLocation() const {
- if (QState *state = sourceState()) {
- QString stateName = state->objectName();
- int transitionIndex = state->transitions().indexOf(const_cast<QScxmlBaseTransition *>(this));
- return QStringLiteral("transition #%1 in state %2").arg(transitionIndex).arg(stateName);
- }
- return QStringLiteral("unbound transition @%1").arg(reinterpret_cast<quintptr>(this));
-}
-
-bool QScxmlBaseTransition::eventTest(QEvent *event)
-{
- Q_D(QScxmlBaseTransition);
-
- if (d->eventSelector.isEmpty())
- return true;
- if (event->type() == QEvent::None)
- return false;
- Q_ASSERT(stateMachine());
- QString eventName = QScxmlStateMachinePrivate::get(stateMachine())->m_event.name();
- bool selected = false;
- foreach (QString eventStr, d->eventSelector) {
- if (eventStr == QStringLiteral("*")) {
- selected = true;
- break;
- }
- if (eventStr.endsWith(QStringLiteral(".*")))
- eventStr.chop(2);
- if (eventName.startsWith(eventStr)) {
- QChar nextC = QLatin1Char('.');
- if (eventName.size() > eventStr.size())
- nextC = eventName.at(eventStr.size());
- if (nextC == QLatin1Char('.') || nextC == QLatin1Char('(')) {
- selected = true;
- break;
- }
- }
- }
- return selected;
-}
-
-void QScxmlBaseTransition::onTransition(QEvent *event)
-{
- Q_UNUSED(event);
-}
-
-QScxmlTransitionPrivate::QScxmlTransitionPrivate()
- : conditionalExp(QScxmlExecutableContent::NoEvaluator)
- , instructionsOnTransition(QScxmlExecutableContent::NoInstruction)
-{}
-
-QScxmlTransitionPrivate::~QScxmlTransitionPrivate()
-{}
-
-QScxmlTransition::QScxmlTransition(QState *sourceState, const QStringList &eventSelector)
- : QScxmlBaseTransition(*new QScxmlTransitionPrivate, sourceState, filterEmpty(eventSelector))
-{}
-
-QScxmlTransition::QScxmlTransition(const QStringList &eventSelector)
- : QScxmlBaseTransition(*new QScxmlTransitionPrivate, Q_NULLPTR, filterEmpty(eventSelector))
-{}
-
-QScxmlTransition::~QScxmlTransition()
-{}
-
-void QScxmlTransition::addTransitionTo(QScxmlState *state)
-{
- state->addTransition(this);
-}
-
-void QScxmlTransition::addTransitionTo(QScxmlStateMachine *stateMachine)
-{
- QScxmlStateMachinePrivate::get(stateMachine)->m_qStateMachine->addTransition(this);
-}
-
-bool QScxmlTransition::eventTest(QEvent *event)
-{
- Q_D(QScxmlTransition);
-
-#ifdef DUMP_EVENT
- if (auto edm = dynamic_cast<QScxmlEcmaScriptDataModel *>(stateMachine()->dataModel()))
- qCDebug(qscxmlLog) << qPrintable(edm->engine()->evaluate(QLatin1String("JSON.stringify(_event)")).toString());
-#endif
-
- if (QScxmlBaseTransition::eventTest(event)) {
- bool ok = true;
- if (d->conditionalExp != QScxmlExecutableContent::NoEvaluator)
- return stateMachine()->dataModel()->evaluateToBool(d->conditionalExp, &ok) && ok;
- return true;
- }
-
- return false;
-}
-
-void QScxmlTransition::onTransition(QEvent *)
-{
- Q_D(QScxmlTransition);
-
- QScxmlStateMachinePrivate::get(stateMachine())->m_executionEngine->execute(d->instructionsOnTransition);
-}
-
-QScxmlStateMachine *QScxmlTransition::stateMachine() const {
- // work around a bug in QStateMachine
- if (QScxmlInternal::WrappedQStateMachine *t = qobject_cast<QScxmlInternal::WrappedQStateMachine *>(sourceState()))
- return t->stateMachine();
- return qobject_cast<QScxmlInternal::WrappedQStateMachine *>(machine())->stateMachine();
-}
-
-void QScxmlTransition::setInstructionsOnTransition(QScxmlExecutableContent::ContainerId instructions)
-{
- Q_D(QScxmlTransition);
- d->instructionsOnTransition = instructions;
-}
-
-void QScxmlTransition::setConditionalExpression(QScxmlExecutableContent::EvaluatorId evaluator)
-{
- Q_D(QScxmlTransition);
- d->conditionalExp = evaluator;
-}
-
-QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlqstates.h b/src/scxml/qscxmlqstates.h
deleted file mode 100644
index 7014861..0000000
--- a/src/scxml/qscxmlqstates.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/****************************************************************************
-**
-** 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 SCXMLQSTATES_H
-#define SCXMLQSTATES_H
-
-#include <QtScxml/qscxmlstatemachine.h>
-#include <QtScxml/qscxmlinvokableservice.h>
-
-#include <QAbstractTransition>
-#include <QFinalState>
-#include <QHistoryState>
-#include <QState>
-
-QT_BEGIN_NAMESPACE
-
-template<class T>
-class QScxmlInvokeScxmlFactory: public QScxmlInvokableScxmlServiceFactory
-{
-public:
- QScxmlInvokeScxmlFactory(QScxmlExecutableContent::StringId invokeLocation,
- QScxmlExecutableContent::EvaluatorId srcexpr,
- QScxmlExecutableContent::StringId id,
- QScxmlExecutableContent::StringId idPrefix,
- QScxmlExecutableContent::StringId idlocation,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- bool doAutoforward,
- const QVector<Param> &params,
- QScxmlExecutableContent::ContainerId finalize)
- : QScxmlInvokableScxmlServiceFactory(invokeLocation,
- srcexpr,
- id,
- idPrefix,
- idlocation,
- namelist,
- doAutoforward,
- params,
- finalize)
- {}
-
- QScxmlInvokableService *invoke(QScxmlStateMachine *parent) Q_DECL_OVERRIDE
- {
- bool ok = true;
- auto srcexpr = calculateSrcexpr(parent, &ok);
- if (!ok)
- return Q_NULLPTR;
-
- if (!srcexpr.isEmpty())
- return loadAndInvokeDynamically(parent, srcexpr);
-
- return finishInvoke(new T, parent);
- }
-};
-
-class QScxmlStatePrivate;
-class Q_SCXML_EXPORT QScxmlState: public QState
-{
- Q_OBJECT
-
-public:
- QScxmlState(QState *parent = Q_NULLPTR);
- QScxmlState(QScxmlStateMachine *parent);
- ~QScxmlState();
-
- void setAsInitialStateFor(QScxmlState *state);
- void setAsInitialStateFor(QScxmlStateMachine *stateMachine);
-
- QScxmlStateMachine *stateMachine() const;
- QString stateLocation() const;
-
- void setInitInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setInvokableServiceFactories(const QVector<QScxmlInvokableServiceFactory *>& factories);
-
-Q_SIGNALS:
- void didEnter(); // TODO: REMOVE!
- void willExit(); // TODO: REMOVE!
-
-protected:
- void onEntry(QEvent * event) Q_DECL_OVERRIDE;
- void onExit(QEvent * event) Q_DECL_OVERRIDE;
-
-private:
- Q_DECLARE_PRIVATE(QScxmlState)
-};
-
-class QScxmlFinalStatePrivate;
-class Q_SCXML_EXPORT QScxmlFinalState: public QFinalState
-{
- Q_OBJECT
-
-public:
- QScxmlFinalState(QState *parent = Q_NULLPTR);
- QScxmlFinalState(QScxmlStateMachine *parent);
- ~QScxmlFinalState();
-
- void setAsInitialStateFor(QScxmlState *state);
- void setAsInitialStateFor(QScxmlStateMachine *stateMachine);
-
- QScxmlStateMachine *stateMachine() const;
-
- QScxmlExecutableContent::ContainerId doneData() const;
- void setDoneData(QScxmlExecutableContent::ContainerId doneData);
-
- void setOnEntryInstructions(QScxmlExecutableContent::ContainerId instructions);
- void setOnExitInstructions(QScxmlExecutableContent::ContainerId instructions);
-
-protected:
- void onEntry(QEvent * event) Q_DECL_OVERRIDE;
- void onExit(QEvent * event) Q_DECL_OVERRIDE;
-
-private:
- Q_DECLARE_PRIVATE(QScxmlFinalState)
-};
-
-class Q_SCXML_EXPORT QScxmlHistoryState: public QHistoryState
-{
- Q_OBJECT
-
-public:
- QScxmlHistoryState(QState *parent = Q_NULLPTR);
- ~QScxmlHistoryState();
-
- void setAsInitialStateFor(QScxmlState *state);
- void setAsInitialStateFor(QScxmlStateMachine *stateMachine);
-
- QScxmlStateMachine *stateMachine() const;
-};
-
-class QScxmlBaseTransitionPrivate;
-class Q_SCXML_EXPORT QScxmlBaseTransition: public QAbstractTransition
-{
- Q_OBJECT
- class Data;
-
-public:
- QScxmlBaseTransition(QState * sourceState = Q_NULLPTR,
- const QStringList &eventSelector = QStringList());
- ~QScxmlBaseTransition();
-
- QScxmlStateMachine *stateMachine() const;
- QString transitionLocation() const;
-
- bool eventTest(QEvent *event) Q_DECL_OVERRIDE;
-
-protected:
- void onTransition(QEvent *event) Q_DECL_OVERRIDE;
-
- QScxmlBaseTransition(QScxmlBaseTransitionPrivate &dd, QState *parent,
- const QStringList &eventSelector = QStringList());
-
-private:
- Q_DECLARE_PRIVATE(QScxmlBaseTransition)
-};
-
-class QScxmlTransitionPrivate;
-class Q_SCXML_EXPORT QScxmlTransition: public QScxmlBaseTransition
-{
- Q_OBJECT
-
-public:
- QScxmlTransition(QState * sourceState = Q_NULLPTR,
- const QStringList &eventSelector = QStringList());
- QScxmlTransition(const QStringList &eventSelector);
- ~QScxmlTransition();
-
- void addTransitionTo(QScxmlState *state);
- void addTransitionTo(QScxmlStateMachine *stateMachine);
-
- bool eventTest(QEvent *event) Q_DECL_OVERRIDE;
- QScxmlStateMachine *stateMachine() const;
-
- void setInstructionsOnTransition(QScxmlExecutableContent::ContainerId instructions);
- void setConditionalExpression(QScxmlExecutableContent::EvaluatorId evaluator);
-
-protected:
- void onTransition(QEvent *event) Q_DECL_OVERRIDE;
-
-private:
- Q_DECLARE_PRIVATE(QScxmlTransition)
-};
-
-QT_END_NAMESPACE
-
-#endif // SCXMLQSTATES_H
diff --git a/src/scxml/qscxmlqstates_p.h b/src/scxml/qscxmlqstates_p.h
deleted file mode 100644
index 02b69b3..0000000
--- a/src/scxml/qscxmlqstates_p.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/****************************************************************************
-**
-** 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 SCXMLQSTATE_P_H
-#define SCXMLQSTATE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtScxml/qscxmlqstates.h>
-#include <QtCore/private/qabstracttransition_p.h>
-#include <QtCore/private/qstate_p.h>
-#include <QtCore/private/qfinalstate_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QScxmlStatePrivate: public QStatePrivate
-{
- Q_DECLARE_PUBLIC(QScxmlState)
-
-public:
- static QScxmlStatePrivate *get(QScxmlState *s) { return s ? s->d_func() : nullptr; }
-
- QScxmlStatePrivate();
- ~QScxmlStatePrivate();
-
- QScxmlExecutableContent::ContainerId initInstructions;
- QScxmlExecutableContent::ContainerId onEntryInstructions;
- QScxmlExecutableContent::ContainerId onExitInstructions;
- QVector<QScxmlInvokableServiceFactory *> invokableServiceFactories;
- QVector<QScxmlInvokableService *> invokedServices;
- QVector<QScxmlInvokableService *> servicesWaitingToStart;
-};
-
-class QScxmlFinalStatePrivate: public QFinalStatePrivate
-{
- Q_DECLARE_PUBLIC(QScxmlFinalState)
-
-public:
- static QScxmlFinalStatePrivate *get(QScxmlFinalState *s) { return s ? s->d_func() : nullptr; }
-
- QScxmlFinalStatePrivate();
- ~QScxmlFinalStatePrivate();
-
- QScxmlExecutableContent::ContainerId doneData;
- QScxmlExecutableContent::ContainerId onEntryInstructions;
- QScxmlExecutableContent::ContainerId onExitInstructions;
-};
-
-class QScxmlBaseTransitionPrivate: public QAbstractTransitionPrivate
-{
- Q_DECLARE_PUBLIC(QScxmlBaseTransition)
-
-public:
- QStringList eventSelector;
-};
-
-class QScxmlTransitionPrivate: public QScxmlBaseTransitionPrivate
-{
-public:
- QScxmlTransitionPrivate();
- ~QScxmlTransitionPrivate();
-
- QScxmlExecutableContent::EvaluatorId conditionalExp;
- QScxmlExecutableContent::ContainerId instructionsOnTransition;
-};
-
-QT_END_NAMESPACE
-
-#endif // SCXMLQSTATE_P_H
diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp
index f945c55..31e620c 100644
--- a/src/scxml/qscxmlstatemachine.cpp
+++ b/src/scxml/qscxmlstatemachine.cpp
@@ -41,7 +41,6 @@
#include "qscxmlexecutablecontent_p.h"
#include "qscxmlevent_p.h"
#include "qscxmlinvokableservice.h"
-#include "qscxmlqstates_p.h"
#include "qscxmldatamodel_p.h"
#include <QAbstractState>
@@ -50,11 +49,9 @@
#include <QHash>
#include <QJSEngine>
#include <QLoggingCategory>
-#include <QState>
#include <QString>
#include <QTimer>
-
-#include <QtCore/private/qstatemachine_p.h>
+#include <QThread>
#include <functional>
@@ -63,115 +60,6 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qscxmlLog, "qt.scxml.statemachine")
Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
-namespace QScxmlInternal {
-class WrappedQStateMachinePrivate: public QStateMachinePrivate
-{
- Q_DECLARE_PUBLIC(WrappedQStateMachine)
-
-public:
- WrappedQStateMachinePrivate(QScxmlStateMachine *stateMachine)
- : m_stateMachine(stateMachine)
- , m_queuedEvents(Q_NULLPTR)
- {}
- ~WrappedQStateMachinePrivate()
- {
- if (m_queuedEvents) {
- foreach (const QueuedEvent &qt, *m_queuedEvents) {
- delete qt.event;
- }
-
- delete m_queuedEvents;
- }
- }
-
- int eventIdForDelayedEvent(const QString &sendId);
-
- QScxmlStateMachine *stateMachine() const
- { return m_stateMachine; }
-
- QScxmlStateMachinePrivate *stateMachinePrivate() const
- { return QScxmlStateMachinePrivate::get(stateMachine()); }
-
-protected: // overrides for QStateMachinePrivate:
- void noMicrostep() Q_DECL_OVERRIDE;
- void processedPendingEvents(bool didChange) Q_DECL_OVERRIDE;
- void beginMacrostep() Q_DECL_OVERRIDE;
- void endMacrostep(bool didChange) Q_DECL_OVERRIDE;
-
- void enterStates(QEvent *event, const QList<QAbstractState*> &exitedStates_sorted,
- const QList<QAbstractState*> &statesToEnter_sorted,
- const QSet<QAbstractState*> &statesForDefaultEntry,
- QHash<QAbstractState *, QVector<QPropertyAssignment> > &propertyAssignmentsForState
-# ifndef QT_NO_ANIMATION
- , const QList<QAbstractAnimation*> &selectedAnimations
-# endif
- ) Q_DECL_OVERRIDE;
- void exitStates(QEvent *event, const QList<QAbstractState *> &statesToExit_sorted,
- const QHash<QAbstractState*, QVector<QPropertyAssignment> > &assignmentsForEnteredStates) Q_DECL_OVERRIDE;
-
- void exitInterpreter() Q_DECL_OVERRIDE;
-
- void emitStateFinished(QState *forState, QFinalState *guiltyState) Q_DECL_OVERRIDE;
- void startupHook() Q_DECL_OVERRIDE;
-
-public: // fields
- QScxmlStateMachine *m_stateMachine;
-
- struct QueuedEvent
- {
- QueuedEvent(QEvent *event = Q_NULLPTR, QStateMachine::EventPriority priority = QStateMachine::NormalPriority)
- : event(event)
- , priority(priority)
- {}
-
- QEvent *event;
- QStateMachine::EventPriority priority;
- };
- QVector<QueuedEvent> *m_queuedEvents;
-};
-
-WrappedQStateMachine::WrappedQStateMachine(QScxmlStateMachine *parent)
- : QStateMachine(*new WrappedQStateMachinePrivate(parent), parent)
-{}
-
-WrappedQStateMachine::WrappedQStateMachine(WrappedQStateMachinePrivate &dd, QScxmlStateMachine *parent)
- : QStateMachine(dd, parent)
-{}
-
-QScxmlStateMachine *WrappedQStateMachine::stateMachine() const
-{
- Q_D(const WrappedQStateMachine);
-
- return d->stateMachine();
-}
-
-QScxmlStateMachinePrivate *WrappedQStateMachine::stateMachinePrivate()
-{
- Q_D(const WrappedQStateMachine);
-
- return d->stateMachinePrivate();
-}
-} // Internal namespace
-
-/*!
- * \class QScxmlEventFilter
- * \brief The QScxmlEventFilter class is an event filter for an SCXML state machine.
- * \since 5.7
- * \inmodule QtScxml
- *
- * An event filter can be used to intercept events generated by an SCXML state machine. By default,
- * the QScxmlStateMachine will have an event filter that will intercept events that are marked as
- * external and that have the type \c qt:signal to emit signals.
- *
- * \sa QScxmlStateMachine
- */
-
-/*!
- * Destroys the SCXML event filter.
- */
-QScxmlEventFilter::~QScxmlEventFilter()
-{}
-
/*!
* \fn QScxmlEventFilter::handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine)
*
@@ -210,7 +98,7 @@ QScxmlEventFilter::~QScxmlEventFilter()
\fn QScxmlStateMachine::eventOccurred(const QScxmlEvent &event)
This signal is emitted when the SCXML event \a event occurs. This signal is
- emitted for all events.
+ emitted for all external events.
\sa externalEventOccurred()
*/
@@ -225,97 +113,108 @@ QScxmlEventFilter::~QScxmlEventFilter()
\sa eventOccurred()
*/
-QAtomicInt QScxmlStateMachinePrivate::m_sessionIdCounter = QAtomicInt(0);
+void QScxmlInternal::EventLoopHook::queueProcessEvents()
+{
+ if (smp->m_isProcessingEvents)
+ return;
+
+ QMetaObject::invokeMethod(this, "doProcessEvents", Qt::QueuedConnection);
+}
+
+void QScxmlInternal::EventLoopHook::doProcessEvents()
+{
+ smp->processEvents();
+}
+
+void QScxmlInternal::EventLoopHook::timerEvent(QTimerEvent *timerEvent)
+{
+ const int timerId = timerEvent->timerId();
+ for (auto it = smp->m_delayedEvents.begin(), eit = smp->m_delayedEvents.end(); it != eit; ++it) {
+ if (it->first == timerId) {
+ QScxmlEvent *scxmlEvent = it->second;
+ smp->m_delayedEvents.erase(it);
+ smp->routeEvent(scxmlEvent);
+ return;
+ }
+ }
+}
+QAtomicInt QScxmlStateMachinePrivate::m_sessionIdCounter = QAtomicInt(0);
QScxmlStateMachinePrivate::QScxmlStateMachinePrivate()
: QObjectPrivate()
, m_sessionId(QScxmlStateMachine::generateSessionId(QStringLiteral("session-")))
, m_isInvoked(false)
, m_isInitialized(false)
+ , m_isProcessingEvents(false)
, m_dataModel(Q_NULLPTR)
, m_loader(&m_defaultLoader)
- , m_dataBinding(QScxmlStateMachine::EarlyBinding)
, m_executionEngine(Q_NULLPTR)
, m_tableData(Q_NULLPTR)
- , m_qStateMachine(Q_NULLPTR)
- , m_eventFilter(Q_NULLPTR)
, m_parentStateMachine(Q_NULLPTR)
+ , m_eventLoopHook(this)
{}
QScxmlStateMachinePrivate::~QScxmlStateMachinePrivate()
{
- qDeleteAll(m_invokedServices);
+ for (const InvokedService &invokedService : m_invokedServices)
+ delete invokedService.service;
+ qDeleteAll(m_cachedFactories);
delete m_executionEngine;
}
-void QScxmlStateMachinePrivate::init()
+QScxmlStateMachinePrivate::ParserData *QScxmlStateMachinePrivate::parserData()
{
- Q_Q(QScxmlStateMachine);
- m_executionEngine = new QScxmlExecutableContent::QScxmlExecutionEngine(q);
- setQStateMachine(new QScxmlInternal::WrappedQStateMachine(q));
- QObject::connect(m_qStateMachine, &QStateMachine::runningChanged,
- q, &QScxmlStateMachine::runningChanged);
- QObject::connect(m_qStateMachine, &QStateMachine::finished,
- q, &QScxmlStateMachine::finished);
-
- // The final state is also a stable state.
- QObject::connect(m_qStateMachine, &QStateMachine::finished,
- q, &QScxmlStateMachine::reachedStableState);
+ if (m_parserData.isNull())
+ m_parserData.reset(new ParserData);
+ return m_parserData.data();
}
-void QScxmlStateMachinePrivate::setQStateMachine(QScxmlInternal::WrappedQStateMachine *stateMachine)
+void QScxmlStateMachinePrivate::addService(int invokingState)
{
- m_qStateMachine = stateMachine;
-}
+ Q_Q(QScxmlStateMachine);
-static QAbstractState *findState(const QString &scxmlName, QStateMachine *parent)
-{
- QList<QObject *> worklist;
- worklist.reserve(parent->children().size() + parent->configuration().size());
- worklist.append(parent);
+ const int arrayId = m_stateTable->state(invokingState).serviceFactoryIds;
+ if (arrayId == StateTable::InvalidIndex)
+ return;
- while (!worklist.isEmpty()) {
- QObject *obj = worklist.takeLast();
- if (QAbstractState *state = qobject_cast<QAbstractState *>(obj)) {
- if (state->objectName() == scxmlName)
- return state;
- }
- worklist.append(obj->children());
+ const auto &ids = m_stateTable->array(arrayId);
+ for (int id : ids) {
+ auto factory = serviceFactory(id);
+ auto service = factory->invoke(q);
+ if (service == nullptr)
+ continue; // service failed to start
+ const QString serviceName = service->name();
+ m_invokedServices[size_t(id)] = { invokingState, service, serviceName };
+ emitServiceChanged(id, service);
+ service->start();
}
-
- return Q_NULLPTR;
}
-QAbstractState *QScxmlStateMachinePrivate::stateByScxmlName(const QString &scxmlName)
+void QScxmlStateMachinePrivate::removeService(int invokingState)
{
- return findState(scxmlName, m_qStateMachine);
-}
-
-QScxmlStateMachinePrivate::ParserData *QScxmlStateMachinePrivate::parserData()
-{
- if (m_parserData.isNull())
- m_parserData.reset(new ParserData);
- return m_parserData.data();
-}
+ const int arrayId = m_stateTable->state(invokingState).serviceFactoryIds;
+ if (arrayId == StateTable::InvalidIndex)
+ return;
-void QScxmlStateMachinePrivate::addService(QScxmlInvokableService *service)
-{
- Q_Q(QScxmlStateMachine);
- Q_ASSERT(!m_invokedServices.contains(service));
- m_invokedServices.append(service);
- q->setService(service->name(), service);
+ for (size_t i = 0, ei = m_invokedServices.size(); i != ei; ++i) {
+ auto &it = m_invokedServices[i];
+ QScxmlInvokableService *service = it.service;
+ if (it.invokingState == invokingState && service != nullptr) {
+ it.service = nullptr;
+ delete service;
+ emitServiceChanged(int(i), nullptr);
+ }
+ }
}
-bool QScxmlStateMachinePrivate::removeService(QScxmlInvokableService *service)
+QScxmlInvokableServiceFactory *QScxmlStateMachinePrivate::serviceFactory(int id)
{
- Q_Q(QScxmlStateMachine);
- Q_ASSERT(m_invokedServices.contains(service));
- if (m_invokedServices.removeOne(service)) {
- q->setService(service->name(), Q_NULLPTR);
- return true;
- }
- return false;
+ Q_ASSERT(id <= m_stateTable->maxServiceId && id >= 0);
+ QScxmlInvokableServiceFactory *& factory = m_cachedFactories[size_t(id)];
+ if (factory == nullptr)
+ factory = m_tableData->serviceFactory(id);
+ return factory;
}
bool QScxmlStateMachinePrivate::executeInitialSetup()
@@ -342,11 +241,14 @@ void QScxmlStateMachinePrivate::routeEvent(QScxmlEvent *event)
} else if (origin.startsWith(QStringLiteral("#_")) && origin != QStringLiteral("#_internal")) {
// route to children
auto originId = origin.midRef(2);
- foreach (QScxmlInvokableService *service, m_invokedServices) {
+ for (auto invokedService : m_invokedServices) {
+ auto service = invokedService.service;
+ if (service == nullptr)
+ continue;
if (service->id() == originId) {
qCDebug(qscxmlLog) << q << "routing event" << event->name()
- << "from" << q->name()
- << "to parent" << service->id();
+ << "from" << q->name()
+ << "to child" << service->id();
service->postEvent(new QScxmlEvent(*event));
}
}
@@ -360,17 +262,67 @@ void QScxmlStateMachinePrivate::postEvent(QScxmlEvent *event)
{
Q_Q(QScxmlStateMachine);
- QStateMachine::EventPriority priority =
- event->eventType() == QScxmlEvent::ExternalEvent ? QStateMachine::NormalPriority
- : QStateMachine::HighPriority;
+ if (!event->name().startsWith(QStringLiteral("done.invoke."))) {
+ for (auto invokedService : m_invokedServices) {
+ auto service = invokedService.service;
+ if (service == nullptr)
+ continue;
+ if (event->invokeId() == service->id()) {
+ setEvent(event);
+ service->finalize();
+ resetEvent();
+ }
+ if (service->autoforward()) {
+ qCDebug(qscxmlLog) << q << "auto-forwarding event" << event->name()
+ << "from" << q->name()
+ << "to child" << service->id();
+ service->postEvent(new QScxmlEvent(*event));
+ }
+ }
+ }
+
+ if (event->eventType() == QScxmlEvent::ExternalEvent)
+ emit q->eventOccurred(*event);
+
+ if (event->originType() == QLatin1String("qt:signal"))
+ emit q->externalEventOccurred(*event);
+
+ const int signalIndex = m_tableData->signalIndexForEvent(event->name());
+ if (signalIndex != -1) {
+ emitSignalForEvent(signalIndex, event->data());
+ delete event;
+ return;
+ }
- if (m_qStateMachine->isRunning()) {
- qCDebug(qscxmlLog) << q << "posting event" << event->name();
- m_qStateMachine->postEvent(event, priority);
+ if (event->eventType() == QScxmlEvent::ExternalEvent) {
+ qCDebug(qscxmlLog) << q << "posting external event" << event->name();
+ m_externalQueue.enqueue(event);
} else {
- qCDebug(qscxmlLog) << q << "queueing event" << event->name();
- m_qStateMachine->queueEvent(event, priority);
+ qCDebug(qscxmlLog) << q << "posting internal event" << event->name();
+ m_internalQueue.enqueue(event);
+ }
+
+ m_eventLoopHook.queueProcessEvents();
+}
+
+void QScxmlStateMachinePrivate::submitDelayedEvent(QScxmlEvent *event)
+{
+ Q_ASSERT(event);
+ Q_ASSERT(event->delay() > 0);
+
+ const int timerId = m_eventLoopHook.startTimer(event->delay());
+ if (timerId == 0) {
+ qWarning("QScxmlStateMachinePrivate::submitDelayedEvent: "
+ "failed to start timer for event '%s' (%p)",
+ qPrintable(event->name()), event);
+ delete event;
+ return;
}
+ m_delayedEvents.push_back(std::make_pair(timerId, event));
+
+ qCDebug(qscxmlLog) << q_func()
+ << ": delayed event" << event->name()
+ << "(" << event << ") got id:" << timerId;
}
/*!
@@ -391,6 +343,781 @@ void QScxmlStateMachinePrivate::submitError(const QString &type, const QString &
q->submitEvent(QScxmlEventBuilder::errorEvent(q, type, msg, sendid));
}
+void QScxmlStateMachinePrivate::start()
+{
+ if (m_stateTable->binding == StateTable::LateBinding)
+ m_isFirstStateEntry.resize(m_stateTable->stateCount, true);
+
+ m_runningState = Starting;
+ Q_ASSERT(m_stateTable->initialTransition != StateTable::InvalidIndex);
+}
+
+void QScxmlStateMachinePrivate::pause()
+{
+ if (isRunnable() && !isPaused())
+ m_runningState = Paused;
+}
+
+void QScxmlStateMachinePrivate::processEvents()
+{
+ if (m_isProcessingEvents || (!isRunnable() && !isPaused()))
+ return;
+
+ m_isProcessingEvents = true;
+
+ Q_Q(QScxmlStateMachine);
+ qCDebug(qscxmlLog) << q_func() << "starting macrostep";
+
+ while (isRunnable() && !isPaused()) {
+ if (m_runningState == Starting) {
+ enterStates({m_stateTable->initialTransition});
+ if (m_runningState == Starting)
+ m_runningState = Running;
+ continue;
+ }
+
+ OrderedSet enabledTransitions;
+ std::vector<int> configurationInDocumentOrder = m_configuration.list();
+ std::sort(configurationInDocumentOrder.begin(), configurationInDocumentOrder.end());
+ selectTransitions(enabledTransitions, configurationInDocumentOrder, nullptr);
+ if (!enabledTransitions.isEmpty()) {
+ microstep(enabledTransitions);
+ } else if (!m_internalQueue.isEmpty()) {
+ auto event = m_internalQueue.dequeue();
+ setEvent(event);
+ selectTransitions(enabledTransitions, configurationInDocumentOrder, event);
+ if (!enabledTransitions.isEmpty()) {
+ microstep(enabledTransitions);
+ }
+ resetEvent();
+ delete event;
+ } else if (!m_externalQueue.isEmpty()) {
+ auto event = m_externalQueue.dequeue();
+ setEvent(event);
+ selectTransitions(enabledTransitions, configurationInDocumentOrder, event);
+ if (!enabledTransitions.isEmpty()) {
+ microstep(enabledTransitions);
+ }
+ resetEvent();
+ delete event;
+ } else {
+ // nothing to do, so:
+ break;
+ }
+ }
+
+ if (!m_statesToInvoke.empty()) {
+ for (int stateId : m_statesToInvoke)
+ addService(stateId);
+ m_statesToInvoke.clear();
+ }
+
+ qCDebug(qscxmlLog) << q_func()
+ << "finished macrostep, runnable:" << isRunnable()
+ << "paused:" << isPaused();
+ emit q->reachedStableState();
+ if (!isRunnable() && !isPaused()) {
+ exitInterpreter();
+ emit q->finished();
+ }
+
+ m_isProcessingEvents = false;
+}
+
+void QScxmlStateMachinePrivate::setEvent(QScxmlEvent *event)
+{
+ Q_ASSERT(event);
+ m_dataModel->setScxmlEvent(*event);
+}
+
+void QScxmlStateMachinePrivate::resetEvent()
+{
+ m_dataModel->setScxmlEvent(QScxmlEvent());
+}
+
+void QScxmlStateMachinePrivate::emitStateActive(int stateIndex, bool active)
+{
+ Q_Q(QScxmlStateMachine);
+ void *args[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&active)) };
+ QMetaObject::activate(q, q->metaObject(), stateIndex, args);
+}
+
+void QScxmlStateMachinePrivate::emitServiceChanged(int machineIndex,
+ QScxmlInvokableService *service)
+{
+ Q_Q(QScxmlStateMachine);
+ void *args[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&service)) };
+ QMetaObject::activate(q, q->metaObject(), machineIndex + m_stateTable->stateCount, args);
+}
+
+void QScxmlStateMachinePrivate::emitSignalForEvent(int signalIndex, const QVariant &data)
+{
+ Q_Q(QScxmlStateMachine);
+ void *args[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&data)) };
+ QMetaObject::activate(q, q->metaObject(),
+ signalIndex + m_stateTable->stateCount + m_stateTable->maxServiceId + 1,
+ args);
+}
+
+QStringList QScxmlStateMachinePrivate::stateNames(const std::vector<int> &stateIndexes) const
+{
+ QStringList names;
+ for (int idx : stateIndexes)
+ names.append(m_tableData->string(m_stateTable->state(idx).name));
+ return names;
+}
+
+std::vector<int> QScxmlStateMachinePrivate::historyStates(int stateIdx) const {
+ const StateTable::Array kids = m_stateTable->array(m_stateTable->state(stateIdx).childStates);
+ std::vector<int> res;
+ if (!kids.isValid()) return res;
+ for (int k : kids) {
+ if (m_stateTable->state(k).isHistoryState())
+ res.push_back(k);
+ }
+ return res;
+}
+
+void QScxmlStateMachinePrivate::exitInterpreter()
+{
+ qCDebug(qscxmlLog) << q_func() << "exiting SCXML processing";
+
+ for (auto it : m_delayedEvents) {
+ m_eventLoopHook.killTimer(it.first);
+ delete it.second;
+ }
+ m_delayedEvents.clear();
+
+ auto statesToExitSorted = m_configuration.list();
+ std::sort(statesToExitSorted.begin(), statesToExitSorted.end(), std::greater<int>());
+ for (int stateIndex : statesToExitSorted) {
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.exitInstructions != StateTable::InvalidIndex) {
+ m_executionEngine->execute(state.exitInstructions);
+ }
+ removeService(stateIndex);
+ if (state.type == StateTable::State::Final && state.parentIsScxmlElement()) {
+ returnDoneEvent(state.doneData);
+ }
+ }
+}
+
+void QScxmlStateMachinePrivate::returnDoneEvent(QScxmlExecutableContent::ContainerId doneData)
+{
+ Q_Q(QScxmlStateMachine);
+
+ m_executionEngine->execute(doneData, QVariant());
+ if (m_isInvoked) {
+ auto e = new QScxmlEvent;
+ e->setName(QStringLiteral("done.invoke.") + q->sessionId());
+ e->setInvokeId(q->sessionId());
+ QScxmlStateMachinePrivate::get(m_parentStateMachine)->postEvent(e);
+ }
+}
+
+bool QScxmlStateMachinePrivate::nameMatch(const StateTable::Array &patterns,
+ QScxmlEvent *event) const
+{
+ if (event->type() == QEvent::None)
+ return false;
+
+ const QString eventName = event->name();
+ bool selected = false;
+ for (int eventSelectorIter = 0; eventSelectorIter < patterns.size(); ++eventSelectorIter) {
+ QString eventStr = m_tableData->string(patterns[eventSelectorIter]);
+ if (eventStr == QStringLiteral("*")) {
+ selected = true;
+ break;
+ }
+ if (eventStr.endsWith(QStringLiteral(".*")))
+ eventStr.chop(2);
+ if (eventName.startsWith(eventStr)) {
+ QChar nextC = QLatin1Char('.');
+ if (eventName.size() > eventStr.size())
+ nextC = eventName.at(eventStr.size());
+ if (nextC == QLatin1Char('.') || nextC == QLatin1Char('(')) {
+ selected = true;
+ break;
+ }
+ }
+ }
+ return selected;
+}
+
+void QScxmlStateMachinePrivate::selectTransitions(OrderedSet &enabledTransitions,
+ const std::vector<int> &configInDocumentOrder,
+ QScxmlEvent *event) const
+{
+ if (event == nullptr) {
+ qCDebug(qscxmlLog) << q_func() << "selectEventlessTransitions";
+ } else {
+ qCDebug(qscxmlLog) << q_func() << "selectTransitions with event"
+ << QScxmlEventPrivate::debugString(event).constData();
+ }
+
+ std::vector<int> states;
+ states.reserve(16);
+ for (int configStateIdx : configInDocumentOrder) {
+ if (m_stateTable->state(configStateIdx).isAtomic()) {
+ states.clear();
+ states.push_back(configStateIdx);
+ getProperAncestors(&states, configStateIdx, -1);
+ for (int stateIdx : states) {
+ bool finishedWithThisConfigState = false;
+
+ if (stateIdx == -1) {
+ // the state machine has no transitions (other than the initial one, which has
+ // already been taken at this point)
+ continue;
+ }
+ const auto &state = m_stateTable->state(stateIdx);
+ const StateTable::Array transitions = m_stateTable->array(state.transitions);
+ if (!transitions.isValid())
+ continue;
+ std::vector<int> sortedTransitions(transitions.size(), -1);
+ std::copy(transitions.begin(), transitions.end(), sortedTransitions.begin());
+ for (int transitionIndex : sortedTransitions) {
+ const StateTable::Transition &t = m_stateTable->transition(transitionIndex);
+ bool enabled = false;
+ if (event == nullptr) {
+ if (t.events == -1) {
+ if (t.condition == -1) {
+ enabled = true;
+ } else {
+ bool ok = false;
+ enabled = m_dataModel->evaluateToBool(t.condition, &ok) && ok;
+ }
+ }
+ } else {
+ if (t.events != -1 && nameMatch(m_stateTable->array(t.events), event)) {
+ if (t.condition == -1) {
+ enabled = true;
+ } else {
+ bool ok = false;
+ enabled = m_dataModel->evaluateToBool(t.condition, &ok) && ok;
+ }
+ }
+ }
+ if (enabled) {
+ enabledTransitions.add(transitionIndex);
+ finishedWithThisConfigState = true;
+ break; // stop iterating over transitions
+ }
+ }
+
+ if (finishedWithThisConfigState)
+ break; // stop iterating over ancestors
+ }
+ }
+ }
+ if (!enabledTransitions.isEmpty())
+ removeConflictingTransitions(&enabledTransitions);
+}
+
+void QScxmlStateMachinePrivate::removeConflictingTransitions(OrderedSet *enabledTransitions) const
+{
+ Q_ASSERT(enabledTransitions);
+
+ auto sortedTransitions = enabledTransitions->takeList();
+ std::sort(sortedTransitions.begin(), sortedTransitions.end(), [this](int t1, int t2) -> bool {
+ auto descendantDepth = [this](int state, int ancestor)->int {
+ int depth = 0;
+ for (int it = state; it != -1; it = m_stateTable->state(it).parent) {
+ if (it == ancestor)
+ break;
+ ++depth;
+ }
+ return depth;
+ };
+
+ const auto &s1 = m_stateTable->transition(t1).source;
+ const auto &s2 = m_stateTable->transition(t2).source;
+ if (s1 == s2) {
+ return t1 < t2;
+ } else if (isDescendant(s1, s2)) {
+ return true;
+ } else if (isDescendant(s2, s1)) {
+ return false;
+ } else {
+ const int lcca = findLCCA({ s1, s2 });
+ const int s1Depth = descendantDepth(s1, lcca);
+ const int s2Depth = descendantDepth(s2, lcca);
+ if (s1Depth == s2Depth)
+ return s1 < s2;
+ else
+ return s1Depth > s2Depth;
+ }
+ });
+
+ OrderedSet filteredTransitions;
+ for (int t1 : sortedTransitions) {
+ OrderedSet transitionsToRemove;
+ bool t1Preempted = false;
+ OrderedSet exitSetT1;
+ computeExitSet({t1}, exitSetT1);
+ const int source1 = m_stateTable->transition(t1).source;
+ for (int t2 : filteredTransitions) {
+ OrderedSet exitSetT2;
+ computeExitSet({t2}, exitSetT2);
+ if (exitSetT1.intersectsWith(exitSetT2)) {
+ const int source2 = m_stateTable->transition(t2).source;
+ if (isDescendant(source1, source2)) {
+ transitionsToRemove.add(t2);
+ } else {
+ t1Preempted = true;
+ break;
+ }
+ }
+ }
+ if (!t1Preempted) {
+ for (int t3 : transitionsToRemove) {
+ filteredTransitions.remove(t3);
+ }
+ filteredTransitions.add(t1);
+ }
+ }
+ *enabledTransitions = filteredTransitions;
+}
+
+void QScxmlStateMachinePrivate::getProperAncestors(std::vector<int> *ancestors, int state1,
+ int state2) const
+{
+ Q_ASSERT(ancestors);
+
+ if (state1 == -1) {
+ return;
+ }
+
+ int parent = state1;
+ do {
+ parent = m_stateTable->state(parent).parent;
+ if (parent == state2) {
+ break;
+ }
+ ancestors->push_back(parent);
+ } while (parent != -1);
+}
+
+void QScxmlStateMachinePrivate::microstep(const OrderedSet &enabledTransitions)
+{
+ if (qscxmlLog().isDebugEnabled()) {
+ qCDebug(qscxmlLog) << q_func()
+ << "starting microstep, configuration:"
+ << stateNames(m_configuration.list());
+ qCDebug(qscxmlLog) << q_func() << "enabled transitions:";
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ QString from = QStringLiteral("(none)");
+ if (transition.source != StateTable::InvalidIndex)
+ from = m_tableData->string(m_stateTable->state(transition.source).name);
+ QStringList to;
+ if (transition.targets == StateTable::InvalidIndex) {
+ to.append(QStringLiteral("(none)"));
+ } else {
+ for (int t : m_stateTable->array(transition.targets))
+ to.append(m_tableData->string(m_stateTable->state(t).name));
+ }
+ qCDebug(qscxmlLog) << q_func() << "\t" << t << ":" << from << "->"
+ << to.join(QLatin1Char(','));
+ }
+ }
+
+ exitStates(enabledTransitions);
+ executeTransitionContent(enabledTransitions);
+ enterStates(enabledTransitions);
+
+ qCDebug(qscxmlLog) << q_func() << "finished microstep, configuration:"
+ << stateNames(m_configuration.list());
+}
+
+void QScxmlStateMachinePrivate::exitStates(const OrderedSet &enabledTransitions)
+{
+ OrderedSet statesToExit;
+ computeExitSet(enabledTransitions, statesToExit);
+ auto statesToExitSorted = statesToExit.takeList();
+ std::sort(statesToExitSorted.begin(), statesToExitSorted.end(), std::greater<int>());
+ qCDebug(qscxmlLog) << q_func() << "exiting states" << stateNames(statesToExitSorted);
+ for (int s : statesToExitSorted) {
+ const auto &state = m_stateTable->state(s);
+ if (state.serviceFactoryIds != StateTable::InvalidIndex)
+ m_statesToInvoke.remove(s);
+ }
+ for (int s : statesToExitSorted) {
+ for (int h : historyStates(s)) {
+ const auto &hState = m_stateTable->state(h);
+ QVector<int> history;
+
+ for (int s0 : m_configuration) {
+ const auto &s0State = m_stateTable->state(s0);
+ if (hState.type == StateTable::State::DeepHistory) {
+ if (s0State.isAtomic() && isDescendant(s0, s))
+ history.append(s0);
+ } else {
+ if (s0State.parent == s)
+ history.append(s0);
+ }
+ }
+
+ m_historyValue[h] = history;
+ }
+ }
+ for (int s : statesToExitSorted) {
+ const auto &state = m_stateTable->state(s);
+ if (state.exitInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(state.exitInstructions);
+ m_configuration.remove(s);
+ emitStateActive(s, false);
+ removeService(s);
+ }
+}
+
+void QScxmlStateMachinePrivate::computeExitSet(const OrderedSet &enabledTransitions,
+ OrderedSet &statesToExit) const
+{
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ if (transition.targets == StateTable::InvalidIndex) {
+ // nothing to do here: there is no exit set
+ } else {
+ const int domain = getTransitionDomain(t);
+ for (int s : m_configuration) {
+ if (isDescendant(s, domain))
+ statesToExit.add(s);
+ }
+ }
+ }
+}
+
+void QScxmlStateMachinePrivate::executeTransitionContent(const OrderedSet &enabledTransitions)
+{
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ if (transition.transitionInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(transition.transitionInstructions);
+ }
+}
+
+void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions)
+{
+ Q_Q(QScxmlStateMachine);
+
+ OrderedSet statesToEnter, statesForDefaultEntry;
+ HistoryContent defaultHistoryContent;
+ computeEntrySet(enabledTransitions, &statesToEnter, &statesForDefaultEntry,
+ &defaultHistoryContent);
+ auto sortedStates = statesToEnter.takeList();
+ std::sort(sortedStates.begin(), sortedStates.end());
+ qCDebug(qscxmlLog) << q_func() << "entering states" << stateNames(sortedStates);
+ for (int s : sortedStates) {
+ const auto &state = m_stateTable->state(s);
+ m_configuration.add(s);
+ if (state.serviceFactoryIds != StateTable::InvalidIndex)
+ m_statesToInvoke.insert(s);
+ if (m_stateTable->binding == StateTable::LateBinding && m_isFirstStateEntry[s]) {
+ if (state.initInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(state.initInstructions);
+ m_isFirstStateEntry[s] = false;
+ }
+ if (state.entryInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(state.entryInstructions);
+ if (statesForDefaultEntry.contains(s)) {
+ const auto &initialTransition = m_stateTable->transition(state.initialTransition);
+ if (initialTransition.transitionInstructions != StateTable::InvalidIndex)
+ m_executionEngine->execute(initialTransition.transitionInstructions);
+ }
+ const int dhc = defaultHistoryContent.value(s);
+ if (dhc != StateTable::InvalidIndex)
+ m_executionEngine->execute(dhc);
+ if (state.type == StateTable::State::Final) {
+ if (state.parentIsScxmlElement()) {
+ m_runningState = Finished;
+ } else {
+ const auto &parent = m_stateTable->state(state.parent);
+ m_executionEngine->execute(state.doneData, m_tableData->string(parent.name));
+ if (parent.parent != StateTable::InvalidIndex) {
+ const auto &grandParent = m_stateTable->state(parent.parent);
+ if (grandParent.isParallel()) {
+ if (allInFinalStates(getChildStates(grandParent))) {
+ auto e = new QScxmlEvent;
+ e->setEventType(QScxmlEvent::InternalEvent);
+ e->setName(QStringLiteral("done.state.")
+ + m_tableData->string(grandParent.name));
+ q->submitEvent(e);
+ }
+ }
+ }
+ }
+ }
+ }
+ for (int s : sortedStates)
+ emitStateActive(s, true);
+}
+
+void QScxmlStateMachinePrivate::computeEntrySet(const OrderedSet &enabledTransitions,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const
+{
+ Q_ASSERT(statesToEnter);
+ Q_ASSERT(statesForDefaultEntry);
+ Q_ASSERT(defaultHistoryContent);
+
+ for (int t : enabledTransitions) {
+ const auto &transition = m_stateTable->transition(t);
+ if (transition.targets == StateTable::InvalidIndex)
+ // targetless transition, so nothing to do
+ continue;
+ for (int s : m_stateTable->array(transition.targets))
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ auto ancestor = getTransitionDomain(t);
+ OrderedSet targets;
+ getEffectiveTargetStates(&targets, t);
+ for (auto s : targets)
+ addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+}
+
+void QScxmlStateMachinePrivate::addDescendantStatesToEnter(
+ int stateIndex, OrderedSet *statesToEnter, OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const
+{
+ Q_ASSERT(statesToEnter);
+ Q_ASSERT(statesForDefaultEntry);
+ Q_ASSERT(defaultHistoryContent);
+
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.isHistoryState()) {
+ HistoryValues::const_iterator historyValueIter = m_historyValue.find(stateIndex);
+ if (historyValueIter != m_historyValue.end()) {
+ auto historyValue = historyValueIter.value();
+ for (int s : historyValue)
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ for (int s : historyValue)
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ } else {
+ const auto transitionIdx = m_stateTable->array(state.transitions)[0];
+ const auto &defaultHistoryTransition = m_stateTable->transition(transitionIdx);
+ defaultHistoryContent->operator[](state.parent) =
+ defaultHistoryTransition.transitionInstructions;
+ StateTable::Array targetStates = m_stateTable->array(defaultHistoryTransition.targets);
+ for (int s : targetStates)
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ for (int s : targetStates)
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+ } else {
+ statesToEnter->add(stateIndex);
+ if (state.isCompound()) {
+ statesForDefaultEntry->add(stateIndex);
+ if (state.initialTransition != StateTable::InvalidIndex) {
+ auto initialTransition = m_stateTable->transition(state.initialTransition);
+ auto initialTransitionTargets = m_stateTable->array(initialTransition.targets);
+ for (int targetStateIndex : initialTransitionTargets)
+ addDescendantStatesToEnter(targetStateIndex, statesToEnter,
+ statesForDefaultEntry, defaultHistoryContent);
+ for (int targetStateIndex : initialTransitionTargets)
+ addAncestorStatesToEnter(targetStateIndex, stateIndex, statesToEnter,
+ statesForDefaultEntry, defaultHistoryContent);
+ }
+ } else {
+ if (state.isParallel()) {
+ for (int child : getChildStates(state)) {
+ if (!hasDescendant(*statesToEnter, child))
+ addDescendantStatesToEnter(child, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+ }
+ }
+ }
+}
+
+void QScxmlStateMachinePrivate::addAncestorStatesToEnter(
+ int stateIndex, int ancestorIndex, OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry, HistoryContent *defaultHistoryContent) const
+{
+ Q_ASSERT(statesToEnter);
+ Q_ASSERT(statesForDefaultEntry);
+ Q_ASSERT(defaultHistoryContent);
+
+ std::vector<int> ancestors;
+ getProperAncestors(&ancestors, stateIndex, ancestorIndex);
+ for (int anc : ancestors) {
+ if (anc == -1) {
+ // we can't enter the state machine itself, so:
+ continue;
+ }
+ statesToEnter->add(anc);
+ const auto &ancState = m_stateTable->state(anc);
+ if (ancState.isParallel()) {
+ for (int child : getChildStates(ancState)) {
+ if (!hasDescendant(*statesToEnter, child))
+ addDescendantStatesToEnter(child, statesToEnter, statesForDefaultEntry,
+ defaultHistoryContent);
+ }
+ }
+ }
+}
+
+std::vector<int> QScxmlStateMachinePrivate::getChildStates(
+ const QScxmlExecutableContent::StateTable::State &state) const
+{
+ std::vector<int> childStates;
+ auto kids = m_stateTable->array(state.childStates);
+ if (kids.isValid()) {
+ childStates.reserve(kids.size());
+ for (int kiddo : kids) {
+ switch (m_stateTable->state(kiddo).type) {
+ case StateTable::State::Normal:
+ case StateTable::State::Final:
+ case StateTable::State::Parallel:
+ childStates.push_back(kiddo);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return childStates;
+}
+
+bool QScxmlStateMachinePrivate::hasDescendant(const OrderedSet &statesToEnter, int childIdx) const
+{
+ for (int s : statesToEnter) {
+ if (isDescendant(s, childIdx))
+ return true;
+ }
+ return false;
+}
+
+bool QScxmlStateMachinePrivate::allDescendants(const OrderedSet &statesToEnter, int childdx) const
+{
+ for (int s : statesToEnter) {
+ if (!isDescendant(s, childdx))
+ return false;
+ }
+ return true;
+}
+
+bool QScxmlStateMachinePrivate::isDescendant(int state1, int state2) const
+{
+ int parent = state1;
+ do {
+ parent = m_stateTable->state(parent).parent;
+ if (parent == state2)
+ return true;
+ } while (parent != -1);
+ return false;
+}
+
+bool QScxmlStateMachinePrivate::allInFinalStates(const std::vector<int> &states) const
+{
+ if (states.empty())
+ return false;
+
+ for (int idx : states) {
+ if (!isInFinalState(idx))
+ return false;
+ }
+
+ return true;
+}
+
+bool QScxmlStateMachinePrivate::someInFinalStates(const std::vector<int> &states) const
+{
+ for (int stateIndex : states) {
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.type == StateTable::State::Final && m_configuration.contains(stateIndex))
+ return true;
+ }
+ return false;
+}
+
+bool QScxmlStateMachinePrivate::isInFinalState(int stateIndex) const
+{
+ const auto &state = m_stateTable->state(stateIndex);
+ if (state.isCompound())
+ return someInFinalStates(getChildStates(state)) && m_configuration.contains(stateIndex);
+ else if (state.isParallel())
+ return allInFinalStates(getChildStates(state));
+ else
+ return false;
+}
+
+int QScxmlStateMachinePrivate::getTransitionDomain(int transitionIndex) const
+{
+ const auto &transition = m_stateTable->transition(transitionIndex);
+ if (transition.source == -1)
+ //oooh, we have the initial transition of the state machine.
+ return -1;
+
+ OrderedSet tstates;
+ getEffectiveTargetStates(&tstates, transitionIndex);
+ if (tstates.isEmpty()) {
+ return StateTable::InvalidIndex;
+ } else {
+ const auto &sourceState = m_stateTable->state(transition.source);
+ if (transition.type == StateTable::Transition::Internal
+ && sourceState.isCompound()
+ && allDescendants(tstates, transition.source)) {
+ return transition.source;
+ } else {
+ tstates.add(transition.source);
+ return findLCCA(std::move(tstates));
+ }
+ }
+}
+
+int QScxmlStateMachinePrivate::findLCCA(OrderedSet &&states) const
+{
+ std::vector<int> ancestors;
+ const int head = *states.begin();
+ OrderedSet tail(std::move(states));
+ tail.removeHead();
+
+ getProperAncestors(&ancestors, head, StateTable::InvalidIndex);
+ for (int anc : ancestors) {
+ if (anc != -1) { // the state machine itself is always compound
+ const auto &ancState = m_stateTable->state(anc);
+ if (!ancState.isCompound())
+ continue;
+ }
+
+ if (allDescendants(tail, anc))
+ return anc;
+ }
+
+ return StateTable::InvalidIndex;
+}
+
+void QScxmlStateMachinePrivate::getEffectiveTargetStates(OrderedSet *targets,
+ int transitionIndex) const
+{
+ Q_ASSERT(targets);
+
+ const auto &transition = m_stateTable->transition(transitionIndex);
+ for (int s : m_stateTable->array(transition.targets)) {
+ const auto &state = m_stateTable->state(s);
+ if (state.isHistoryState()) {
+ HistoryValues::const_iterator historyValueIter = m_historyValue.find(s);
+ if (historyValueIter != m_historyValue.end()) {
+ foreach (int historyState, historyValueIter.value()) {
+ targets->add(historyState);
+ }
+ } else {
+ getEffectiveTargetStates(targets, m_stateTable->array(state.transitions)[0]);
+ }
+ } else {
+ targets->add(s);
+ }
+ }
+}
+
/*!
* Creates a state machine from the SCXML file specified by \a fileName.
*
@@ -449,13 +1176,14 @@ QScxmlStateMachine::QScxmlStateMachine(QObject *parent)
: QObject(*new QScxmlStateMachinePrivate, parent)
{
Q_D(QScxmlStateMachine);
- d->init();
+ d->m_executionEngine = new QScxmlExecutableContent::QScxmlExecutionEngine(this);
}
QScxmlStateMachine::QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *parent)
: QObject(dd, parent)
{
- dd.init();
+ Q_D(QScxmlStateMachine);
+ d->m_executionEngine = new QScxmlExecutableContent::QScxmlExecutionEngine(this);
}
/*!
@@ -626,27 +1354,6 @@ QScxmlParser::Loader *QScxmlStateMachine::loader() const
/*!
* \internal
- * Sets the binding method to the specified value.
- */
-void QScxmlStateMachine::setDataBinding(QScxmlStateMachine::BindingMethod bindingMethod)
-{
- Q_D(QScxmlStateMachine);
-
- d->m_dataBinding = bindingMethod;
-}
-
-/*!
- * Returns the binding method used by the state machine.
- */
-QScxmlStateMachine::BindingMethod QScxmlStateMachine::dataBinding() const
-{
- Q_D(const QScxmlStateMachine);
-
- return d->m_dataBinding;
-}
-
-/*!
- * \internal
*
* This is used internally in order to execute the executable content.
*
@@ -672,271 +1379,23 @@ void QScxmlStateMachine::setTableData(QScxmlTableData *tableData)
Q_ASSERT(tableData);
d->m_tableData = tableData;
+ d->m_stateTable = reinterpret_cast<const QScxmlExecutableContent::StateTable *>(
+ tableData->stateMachineTable());
if (objectName().isEmpty()) {
setObjectName(tableData->name());
}
-}
-
-void QScxmlInternal::WrappedQStateMachine::beginSelectTransitions(QEvent *event)
-{
- Q_D(WrappedQStateMachine);
-
- if (event && event->type() == QScxmlEvent::scxmlEventType) {
- stateMachinePrivate()->m_event = *static_cast<QScxmlEvent *>(event);
- d->stateMachine()->dataModel()->setScxmlEvent(stateMachinePrivate()->m_event);
-
- auto scxmlEvent = static_cast<QScxmlEvent *>(event);
- auto smp = stateMachinePrivate();
-
- foreach (QScxmlInvokableService *service, smp->invokedServices()) {
- if (scxmlEvent->invokeId() == service->id()) {
- service->finalize();
- }
- if (service->autoforward()) {
- qCDebug(qscxmlLog) << this << "auto-forwarding event" << scxmlEvent->name()
- << "from" << stateMachine()->name() << "to service" << service->id();
- service->postEvent(new QScxmlEvent(*scxmlEvent));
- }
- }
-
- if (scxmlEvent->eventType() == QScxmlEvent::ExternalEvent) {
- emit d->stateMachine()->eventOccurred(*scxmlEvent);
- }
-
- if (scxmlEvent->originType() == QLatin1String("qt:signal")) {
- emit d->stateMachine()->externalEventOccurred(*scxmlEvent);
- }
-
- if (smp->m_eventFilter && !smp->m_eventFilter->handle(scxmlEvent, d->stateMachine())) {
- scxmlEvent->makeIgnorable();
- scxmlEvent->clear();
- smp->m_event.clear();
- return;
- }
- } else {
- stateMachinePrivate()->m_event.clear();
- d->stateMachine()->dataModel()->setScxmlEvent(stateMachinePrivate()->m_event);
+ if (d->m_stateTable->maxServiceId != QScxmlExecutableContent::StateTable::InvalidIndex) {
+ const size_t serviceCount = size_t(d->m_stateTable->maxServiceId + 1);
+ d->m_invokedServices.resize(serviceCount, { -1, nullptr, QString() });
+ d->m_cachedFactories.resize(serviceCount, nullptr);
}
-}
-
-void QScxmlInternal::WrappedQStateMachine::beginMicrostep(QEvent *event)
-{
- Q_D(WrappedQStateMachine);
- qCDebug(qscxmlLog) << d->m_stateMachine
- << "started microstep from state" << d->m_stateMachine->activeStateNames()
- << "with event" << stateMachinePrivate()->m_event.name()
- << "and event type" << event->type();
-}
-
-void QScxmlInternal::WrappedQStateMachine::endMicrostep(QEvent *event)
-{
- Q_D(WrappedQStateMachine);
- Q_UNUSED(event);
-
- qCDebug(qscxmlLog) << d->m_stateMachine
- << "finished microstep in state (" << d->m_stateMachine->activeStateNames() << ")";
-}
-
-// This is a slightly modified copy of QStateMachinePrivate::event()
-// Instead of postExternalEvent and processEvents
-// we route event first to the appropriate state machine instance.
-bool QScxmlInternal::WrappedQStateMachine::event(QEvent *e)
-{
- Q_D(QScxmlInternal::WrappedQStateMachine);
- if (e->type() == QEvent::Timer) {
- QTimerEvent *te = static_cast<QTimerEvent*>(e);
- int tid = te->timerId();
- if (d->state != QStateMachinePrivate::Running) {
- // This event has been cancelled already
- QMutexLocker locker(&d->delayedEventsMutex);
- Q_ASSERT(!d->timerIdToDelayedEventId.contains(tid));
- return true;
- }
- d->delayedEventsMutex.lock();
- int id = d->timerIdToDelayedEventId.take(tid);
- QStateMachinePrivate::DelayedEvent ee = d->delayedEvents.take(id);
- if (ee.event != 0) {
- Q_ASSERT(ee.timerId == tid);
-// killTimer(tid);
-// d->delayedEventIdFreeList.release(id);
- d->delayedEventsMutex.unlock();
- d->_q_killDelayedEventTimer(id, tid);
- // route here
- if (ee.event->type() == QScxmlEvent::scxmlEventType)
- QScxmlStateMachinePrivate::get(stateMachine())->routeEvent(static_cast<QScxmlEvent *>(ee.event));
-// d->postExternalEvent(ee.event);
-// d->processEvents(QStateMachinePrivate::DirectProcessing);
- return true;
- } else {
- d->delayedEventsMutex.unlock();
- }
- }
- return QState::event(e);
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::noMicrostep()
-{
- qCDebug(qscxmlLog) << m_stateMachine
- << "had no transition, stays in state (" << m_stateMachine->activeStateNames() << ")";
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::processedPendingEvents(bool didChange)
-{
- qCDebug(qscxmlLog) << m_stateMachine << "finishedPendingEvents" << didChange << "in state ("
- << m_stateMachine->activeStateNames() << ")";
- emit m_stateMachine->reachedStableState();
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::beginMacrostep()
-{
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::endMacrostep(bool didChange)
-{
- qCDebug(qscxmlLog) << m_stateMachine << "endMacrostep" << didChange
- << "in state (" << m_stateMachine->activeStateNames() << ")";
-
- { // handle <invoke>s
- QVector<QScxmlState*> &sti = stateMachinePrivate()->m_statesToInvoke;
- std::sort(sti.begin(), sti.end(), WrappedQStateMachinePrivate::stateEntryLessThan);
- foreach (QScxmlState *s, sti) {
- auto sp = QScxmlStatePrivate::get(s);
- foreach (QScxmlInvokableService *s, sp->servicesWaitingToStart) {
- s->start();
- }
- sp->servicesWaitingToStart.clear();
- }
- sti.clear();
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::enterStates(
- QEvent *event,
- const QList<QAbstractState*> &exitedStates_sorted,
- const QList<QAbstractState*> &statesToEnter_sorted,
- const QSet<QAbstractState*> &statesForDefaultEntry,
- QHash<QAbstractState *, QVector<QPropertyAssignment> > &propertyAssignmentsForState
-# ifndef QT_NO_ANIMATION
- , const QList<QAbstractAnimation*> &selectedAnimations
-# endif
- )
-{
- QStateMachinePrivate::enterStates(event, exitedStates_sorted, statesToEnter_sorted,
- statesForDefaultEntry, propertyAssignmentsForState
-# ifndef QT_NO_ANIMATION
- , selectedAnimations
-# endif
- );
- foreach (QAbstractState *s, statesToEnter_sorted) {
- if (QScxmlState *qss = qobject_cast<QScxmlState *>(s)) {
- if (!QScxmlStatePrivate::get(qss)->invokableServiceFactories.isEmpty()) {
- if (!stateMachinePrivate()->m_statesToInvoke.contains(qss)) {
- stateMachinePrivate()->m_statesToInvoke.append(qss);
- }
- }
- }
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::exitStates(
- QEvent *event,
- const QList<QAbstractState *> &statesToExit_sorted,
- const QHash<QAbstractState*, QVector<QPropertyAssignment> > &assignmentsForEnteredStates)
-{
- QStateMachinePrivate::exitStates(event, statesToExit_sorted, assignmentsForEnteredStates);
-
- auto smp = stateMachinePrivate();
- for (int i = 0; i < smp->m_statesToInvoke.size(); ) {
- if (statesToExit_sorted.contains(smp->m_statesToInvoke.at(i))) {
- smp->m_statesToInvoke.removeAt(i);
- } else {
- ++i;
- }
- }
-
- foreach (QAbstractState *s, statesToExit_sorted) {
- if (QScxmlState *qss = qobject_cast<QScxmlState *>(s)) {
- auto ssp = QScxmlStatePrivate::get(qss);
- ssp->servicesWaitingToStart.clear();
- QVector<QScxmlInvokableService *> &services = ssp->invokedServices;
- foreach (QScxmlInvokableService *service, services) {
- qCDebug(qscxmlLog) << stateMachine() << "schedule service cancellation" << service->id();
- QMetaObject::invokeMethod(q_func(),
- "removeAndDestroyService",
- Qt::QueuedConnection,
- Q_ARG(QScxmlInvokableService *,service));
- }
- services.clear();
- }
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::exitInterpreter()
-{
- Q_Q(WrappedQStateMachine);
-
- foreach (QAbstractState *s, configuration) {
- QScxmlExecutableContent::ContainerId onExitInstructions = QScxmlExecutableContent::NoInstruction;
- if (QScxmlFinalState *finalState = qobject_cast<QScxmlFinalState *>(s)) {
- stateMachinePrivate()->m_executionEngine->execute(finalState->doneData(), QVariant());
- onExitInstructions = QScxmlFinalStatePrivate::get(finalState)->onExitInstructions;
- } else if (QScxmlState *state = qobject_cast<QScxmlState *>(s)) {
- onExitInstructions = QScxmlStatePrivate::get(state)->onExitInstructions;
- }
-
- if (onExitInstructions != QScxmlExecutableContent::NoInstruction) {
- stateMachinePrivate()->m_executionEngine->execute(onExitInstructions);
- }
-
- if (QScxmlFinalState *finalState = qobject_cast<QScxmlFinalState *>(s)) {
- if (finalState->parent() == q) {
- if (auto psm = stateMachinePrivate()->m_parentStateMachine) {
- auto done = new QScxmlEvent;
- done->setName(QStringLiteral("done.invoke.") + m_stateMachine->sessionId());
- done->setInvokeId(m_stateMachine->sessionId());
- qCDebug(qscxmlLog) << "submitting event" << done->name() << "to" << psm->name();
- psm->submitEvent(done);
- }
- }
- }
- }
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::emitStateFinished(QState *forState, QFinalState *guiltyState)
-{
- Q_Q(WrappedQStateMachine);
-
- if (QScxmlFinalState *finalState = qobject_cast<QScxmlFinalState *>(guiltyState)) {
- if (!q->isRunning())
- return;
- stateMachinePrivate()->m_executionEngine->execute(finalState->doneData(), forState->objectName());
- }
-
- QStateMachinePrivate::emitStateFinished(forState, guiltyState);
-}
-
-void QScxmlInternal::WrappedQStateMachinePrivate::startupHook()
-{
- Q_Q(WrappedQStateMachine);
-
- q->submitQueuedEvents();
-}
-
-int QScxmlInternal::WrappedQStateMachinePrivate::eventIdForDelayedEvent(const QString &sendId)
-{
- QMutexLocker locker(&delayedEventsMutex);
-
- QHash<int, DelayedEvent>::const_iterator it;
- for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
- if (QScxmlEvent *e = dynamic_cast<QScxmlEvent *>(it->event)) {
- if (e->sendId() == sendId) {
- return it.key();
- }
- }
- }
-
- return -1;
+ if (d->m_stateTable->version != Q_QSCXMLC_OUTPUT_REVISION)
+ qFatal("Cannot mix incompatible state table (version 0x%x) with this library (version 0x%x)",
+ d->m_stateTable->version, Q_QSCXMLC_OUTPUT_REVISION);
+ Q_ASSERT(tableData->stateMachineTable()[d->m_stateTable->arrayOffset +
+ d->m_stateTable->arraySize]
+ == QScxmlExecutableContent::StateTable::terminator);
}
/*!
@@ -947,26 +1406,21 @@ int QScxmlInternal::WrappedQStateMachinePrivate::eventIdForDelayedEvent(const QS
* When it is \c false, the full list of all states will be returned.
*
* The returned list does not contain the states of possible nested state machines.
+ *
+ * \note The order of the state names in the list is the order in which the states occurred in
+ * the SCXML document.
*/
QStringList QScxmlStateMachine::stateNames(bool compress) const
{
Q_D(const QScxmlStateMachine);
- QList<QObject *> worklist;
- worklist.reserve(d->m_qStateMachine->children().size() + d->m_qStateMachine->configuration().size());
- worklist.append(d->m_qStateMachine->children());
-
- QStringList res;
- while (!worklist.isEmpty()) {
- QObject *obj = worklist.takeLast();
- if (QAbstractState *state = qobject_cast<QAbstractState *>(obj)) {
- if (!compress || !obj->children().count())
- res.append(state->objectName());
- worklist.append(obj->children());
- }
+ QStringList names;
+ for (int i = 0; i < d->m_stateTable->stateCount; ++i) {
+ const auto &state = d->m_stateTable->state(i);
+ if (!compress || state.isAtomic())
+ names.append(d->m_tableData->string(state.name));
}
- std::sort(res.begin(), res.end());
- return res;
+ return names;
}
/*!
@@ -980,19 +1434,13 @@ QStringList QScxmlStateMachine::activeStateNames(bool compress) const
{
Q_D(const QScxmlStateMachine);
- QSet<QAbstractState *> config = QStateMachinePrivate::get(d->m_qStateMachine)->configuration;
- if (compress)
- foreach (const QAbstractState *s, config)
- config.remove(s->parentState());
- QStringList res;
- foreach (const QAbstractState *s, config) {
- QString id = s->objectName();
- if (!id.isEmpty()) {
- res.append(id);
- }
+ QStringList result;
+ for (int stateIdx : d->m_configuration) {
+ const auto &state = d->m_stateTable->state(stateIdx);
+ if (state.isAtomic() || !compress)
+ result.append(d->m_tableData->string(state.name));
}
- std::sort(res.begin(), res.end());
- return res;
+ return result;
}
/*!
@@ -1001,20 +1449,20 @@ QStringList QScxmlStateMachine::activeStateNames(bool compress) const
bool QScxmlStateMachine::isActive(const QString &scxmlStateName) const
{
Q_D(const QScxmlStateMachine);
- QSet<QAbstractState *> config = QStateMachinePrivate::get(d->m_qStateMachine)->configuration;
- foreach (QAbstractState *s, config) {
- if (s->objectName() == scxmlStateName) {
+
+ for (int stateIndex : d->m_configuration) {
+ const auto &state = d->m_stateTable->state(stateIndex);
+ if (d->m_tableData->string(state.name) == scxmlStateName)
return true;
- }
}
+
return false;
}
/*!
* Creates a connection of the given \a type from the state identified by \a scxmlStateName
* to the \a method in the \a receiver object. The receiver's \a method
- * may contain a boolean argument that indicates whether the state connected
- * became active or inactive.
+ * may contain a boolean argument that indicates whether the state connected * became active or inactive.
*
* Returns a handle to the connection, which can be used later to disconnect.
*/
@@ -1022,27 +1470,9 @@ QMetaObject::Connection QScxmlStateMachine::connectToState(const QString &scxmlS
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
- Q_D(QScxmlStateMachine);
- QAbstractState *state = findState(scxmlStateName, d->m_qStateMachine);
- return QObject::connect(state, SIGNAL(activeChanged(bool)), receiver, method, type);
-}
-
-/*!
- * Returns the SCXML event filter if one is set, otherwise returns null.
- */
-QScxmlEventFilter *QScxmlStateMachine::scxmlEventFilter() const
-{
- Q_D(const QScxmlStateMachine);
- return d->m_eventFilter;
-}
-
-/*!
- * Sets the \a newFilter as the SCXML event filter. Passing null will remove the current filter.
- */
-void QScxmlStateMachine::setScxmlEventFilter(QScxmlEventFilter *newFilter)
-{
- Q_D(QScxmlStateMachine);
- d->m_eventFilter = newFilter;
+ QByteArray signalName = QByteArray::number(QSIGNAL_CODE) + scxmlStateName.toUtf8()
+ + "Changed(bool)";
+ return QObject::connect(this, signalName.constData(), receiver, method, type);
}
/*!
@@ -1085,7 +1515,7 @@ bool QScxmlStateMachine::isRunning() const
{
Q_D(const QScxmlStateMachine);
- return d->m_qStateMachine->isRunning();
+ return d->isRunnable() && !d->isPaused();
}
/*!
@@ -1143,9 +1573,7 @@ void QScxmlStateMachine::submitEvent(QScxmlEvent *event)
<< QScxmlEventPrivate::debugString(event).constData();
Q_ASSERT(event->eventType() == QScxmlEvent::ExternalEvent);
- int id = d->m_qStateMachine->postDelayedEvent(event, event->delay());
-
- qCDebug(qscxmlLog) << this << ": delayed event" << event->name() << "(" << event << ") got id:" << id;
+ d->submitDelayedEvent(event);
} else {
qCDebug(qscxmlLog) << this << "submitting event" << event->name()
<< ":" << QScxmlEventPrivate::debugString(event).constData();
@@ -1173,9 +1601,8 @@ void QScxmlStateMachine::submitEvent(const QString &eventName)
void QScxmlStateMachine::submitEvent(const QString &eventName, const QVariant &data)
{
QVariant incomingData = data;
- if (incomingData.canConvert<QJSValue>()) {
+ if (incomingData.canConvert<QJSValue>())
incomingData = incomingData.value<QJSValue>().toVariant();
- }
QScxmlEvent *e = new QScxmlEvent;
e->setName(eventName);
@@ -1191,49 +1618,16 @@ void QScxmlStateMachine::cancelDelayedEvent(const QString &sendId)
{
Q_D(QScxmlStateMachine);
- int id = d->m_qStateMachine->eventIdForDelayedEvent(sendId);
-
- qCDebug(qscxmlLog) << this << "canceling event" << sendId << "with id" << id;
-
- if (id != -1)
- d->m_qStateMachine->cancelDelayedEvent(id);
-}
-
-void QScxmlInternal::WrappedQStateMachine::queueEvent(QScxmlEvent *event, EventPriority priority)
-{
- Q_D(WrappedQStateMachine);
-
- if (!d->m_queuedEvents)
- d->m_queuedEvents = new QVector<WrappedQStateMachinePrivate::QueuedEvent>();
- d->m_queuedEvents->append(WrappedQStateMachinePrivate::QueuedEvent(event, priority));
-}
-
-void QScxmlInternal::WrappedQStateMachine::submitQueuedEvents()
-{
- Q_D(WrappedQStateMachine);
-
- qCDebug(qscxmlLog) << d->m_stateMachine << ": submitting queued events";
-
- if (d->m_queuedEvents) {
- foreach (const WrappedQStateMachinePrivate::QueuedEvent &e, *d->m_queuedEvents)
- postEvent(e.event, e.priority);
- delete d->m_queuedEvents;
- d->m_queuedEvents = Q_NULLPTR;
- }
-}
-
-int QScxmlInternal::WrappedQStateMachine::eventIdForDelayedEvent(const QString &sendId)
-{
- Q_D(WrappedQStateMachine);
- return d->eventIdForDelayedEvent(sendId);
-}
-
-void QScxmlInternal::WrappedQStateMachine::removeAndDestroyService(QScxmlInvokableService *service)
-{
- Q_D(WrappedQStateMachine);
- qCDebug(qscxmlLog) << stateMachine() << "canceling service" << service->id();
- if (d->stateMachinePrivate()->removeService(service)) {
- delete service;
+ for (auto it = d->m_delayedEvents.begin(), eit = d->m_delayedEvents.end(); it != eit; ++it) {
+ if (it->second->sendId() == sendId) {
+ qCDebug(qscxmlLog) << this
+ << "canceling event" << sendId
+ << "with timer id" << it->first;
+ d->m_eventLoopHook.killTimer(it->first);
+ delete it->second;
+ d->m_delayedEvents.erase(it);
+ return;
+ }
}
}
@@ -1262,10 +1656,9 @@ bool QScxmlStateMachine::isDispatchableTarget(const QString &target) const
if (target.startsWith(QStringLiteral("#_"))) {
QStringRef targetId = target.midRef(2);
- foreach (QScxmlInvokableService *service, d->m_invokedServices) {
- if (service->id() == targetId) {
+ for (auto invokedService : d->m_invokedServices) {
+ if (invokedService.service->id() == targetId)
return true;
- }
}
}
@@ -1324,7 +1717,8 @@ void QScxmlStateMachine::start()
if (!isInitialized() && !init())
qCDebug(qscxmlLog) << this << "cannot be initialized on start(). Starting anyway ...";
- d->m_qStateMachine->start();
+ d->start();
+ d->m_eventLoopHook.queueProcessEvents();
}
/*!
@@ -1336,16 +1730,23 @@ void QScxmlStateMachine::start()
void QScxmlStateMachine::stop()
{
Q_D(QScxmlStateMachine);
- d->m_qStateMachine->stop();
+ d->pause();
}
-/*!
- * \internal
- */
-void QScxmlStateMachine::setService(const QString &id, QScxmlInvokableService *service)
+bool QScxmlStateMachine::isActive(int stateIndex) const
{
- Q_UNUSED(id);
- Q_UNUSED(service);
+ Q_D(const QScxmlStateMachine);
+ return d->m_configuration.contains(stateIndex);
+}
+
+QScxmlStateMachine *QScxmlStateMachine::subStateMachine(int index) const
+{
+ Q_D(const QScxmlStateMachine);
+ auto invokedService = d->m_invokedServices[size_t(index)].service;
+ if (auto scxmlService = dynamic_cast<QScxmlInvokableScxml *>(invokedService))
+ return scxmlService->stateMachine();
+ else
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachine.h b/src/scxml/qscxmlstatemachine.h
index 2ab7880..7a95f82 100644
--- a/src/scxml/qscxmlstatemachine.h
+++ b/src/scxml/qscxmlstatemachine.h
@@ -61,13 +61,6 @@ class QScxmlInvokableService;
class QScxmlStateMachine;
class QScxmlTableData;
-class Q_SCXML_EXPORT QScxmlEventFilter
-{
-public:
- virtual ~QScxmlEventFilter();
- virtual bool handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine) = 0;
-};
-
class QScxmlStateMachinePrivate;
class Q_SCXML_EXPORT QScxmlStateMachine: public QObject
{
@@ -86,11 +79,6 @@ protected:
#endif // Q_QDOC
public:
- enum BindingMethod {
- EarlyBinding,
- LateBinding
- };
-
static QScxmlStateMachine *fromFile(const QString &fileName);
static QScxmlStateMachine *fromData(QIODevice *data, const QString &fileName = QString());
QVector<QScxmlError> parseErrors() const;
@@ -108,8 +96,6 @@ public:
void setLoader(QScxmlParser::Loader *loader);
QScxmlParser::Loader *loader() const;
- BindingMethod dataBinding() const;
-
bool isRunning() const;
void setRunning(bool running);
@@ -125,9 +111,6 @@ public:
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection);
- QScxmlEventFilter *scxmlEventFilter() const;
- void setScxmlEventFilter(QScxmlEventFilter *newFilter);
-
Q_INVOKABLE void submitEvent(QScxmlEvent *event);
Q_INVOKABLE void submitEvent(const QString &eventName);
Q_INVOKABLE void submitEvent(const QString &eventName, const QVariant &data);
@@ -158,9 +141,9 @@ protected: // methods for friends:
friend QScxmlExecutableContent::QScxmlExecutionEngine;
#ifndef Q_QDOC
- void setDataBinding(BindingMethod bindingMethod);
- virtual void setService(const QString &id, QScxmlInvokableService *service);
-
+ // The methods below are used by the compiled state machines.
+ bool isActive(int stateIndex) const;
+ QScxmlStateMachine *subStateMachine(int index) const;
QScxmlTableData *tableData() const;
void setTableData(QScxmlTableData *tableData);
#endif // Q_QDOC
diff --git a/src/scxml/qscxmlstatemachine_p.h b/src/scxml/qscxmlstatemachine_p.h
index ae68ce5..d739e53 100644
--- a/src/scxml/qscxmlstatemachine_p.h
+++ b/src/scxml/qscxmlstatemachine_p.h
@@ -53,44 +53,32 @@
#include <QtScxml/private/qscxmlexecutablecontent_p.h>
#include <QtScxml/qscxmlstatemachine.h>
-
-#include <QStateMachine>
-#include <QtCore/private/qstatemachine_p.h>
+#include <QtCore/private/qobject_p.h>
QT_BEGIN_NAMESPACE
namespace QScxmlInternal {
-class WrappedQStateMachinePrivate;
-class WrappedQStateMachine: public QStateMachine
+class EventLoopHook: public QObject
{
Q_OBJECT
- Q_DECLARE_PRIVATE(WrappedQStateMachine)
-public:
- WrappedQStateMachine(QScxmlStateMachine *parent);
- WrappedQStateMachine(WrappedQStateMachinePrivate &dd, QScxmlStateMachine *parent);
+ QScxmlStateMachinePrivate *smp;
- QScxmlStateMachine *stateMachine() const;
+public:
+ EventLoopHook(QScxmlStateMachinePrivate *smp)
+ : smp(smp)
+ {}
- void queueEvent(QScxmlEvent *event, QStateMachine::EventPriority priority);
- void submitQueuedEvents();
- int eventIdForDelayedEvent(const QString &sendId);
+ void queueProcessEvents();
- Q_INVOKABLE void removeAndDestroyService(QScxmlInvokableService *service);
+ Q_INVOKABLE void doProcessEvents();
protected:
- void beginSelectTransitions(QEvent *event) Q_DECL_OVERRIDE;
- void beginMicrostep(QEvent *event) Q_DECL_OVERRIDE;
- void endMicrostep(QEvent *event) Q_DECL_OVERRIDE;
- bool event(QEvent *e) Q_DECL_OVERRIDE;
-
-private:
- QScxmlStateMachinePrivate *stateMachinePrivate();
+ void timerEvent(QTimerEvent *timerEvent) Q_DECL_OVERRIDE;
};
-} // Internal namespace
+} // QScxmlInternal namespace
class QScxmlInvokableService;
-class QScxmlState;
class QScxmlStateMachinePrivate: public QObjectPrivate
{
Q_DECLARE_PUBLIC(QScxmlStateMachine)
@@ -98,6 +86,27 @@ class QScxmlStateMachinePrivate: public QObjectPrivate
static QAtomicInt m_sessionIdCounter;
public: // types
+ typedef QScxmlExecutableContent::StateTable StateTable;
+
+ class HistoryContent
+ {
+ QHash<int, int> storage;
+
+ public:
+ HistoryContent() { storage.reserve(4); }
+
+ int &operator[](int idx) {
+ QHash<int, int>::Iterator i = storage.find(idx);
+ return (i == storage.end()) ? storage.insert(idx, StateTable::InvalidIndex).value() :
+ i.value();
+ }
+
+ int value(int idx) const {
+ QHash<int, int>::ConstIterator i = storage.constFind(idx);
+ return (i == storage.constEnd()) ? StateTable::InvalidIndex : i.value();
+ }
+ };
+
class ParserData
{
public:
@@ -105,57 +114,221 @@ public: // types
QVector<QScxmlError> m_errors;
};
+ // The OrderedSet is a set where it elements are in insertion order. See
+ // http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation under Algorithm, Datatypes. It
+ // is used to keep lists of states and transitions in document order.
+ class OrderedSet
+ {
+ std::vector<int> storage;
+
+ public:
+ OrderedSet(){}
+ OrderedSet(std::initializer_list<int> l): storage(l) {}
+
+ std::vector<int> takeList() const
+ { return std::move(storage); }
+
+ const std::vector<int> &list() const
+ { return storage; }
+
+ bool contains(int i) const
+ {
+ return std::find(storage.cbegin(), storage.cend(), i) != storage.cend();
+ }
+
+ bool remove(int i)
+ {
+ std::vector<int>::iterator it = std::find(storage.begin(), storage.end(), i);
+ if (it == storage.end()) {
+ return false;
+ }
+ storage.erase(it);
+ return true;
+ }
+
+ void removeHead()
+ { if (!isEmpty()) storage.erase(storage.begin()); }
+
+ bool isEmpty() const
+ { return storage.empty(); }
+
+ void add(int i)
+ { if (!contains(i)) storage.push_back(i); }
+
+ bool intersectsWith(const OrderedSet &other) const
+ {
+ for (auto i : storage) {
+ if (other.contains(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void clear()
+ { storage.clear(); }
+
+ typedef std::vector<int>::const_iterator const_iterator;
+ const_iterator begin() const { return storage.cbegin(); }
+ const_iterator end() const { return storage.cend(); }
+ };
+
+ class Queue
+ {
+ QVector<QScxmlEvent *> storage;
+
+ public:
+ Queue()
+ { storage.reserve(4); }
+
+ ~Queue()
+ { qDeleteAll(storage); }
+
+ void enqueue(QScxmlEvent *e)
+ { storage.append(e); }
+
+ bool isEmpty() const
+ { return storage.empty(); }
+
+ QScxmlEvent *dequeue()
+ {
+ Q_ASSERT(!isEmpty());
+ QScxmlEvent *e = storage.first();
+ storage.pop_front();
+ int sz = storage.size();
+ if (Q_UNLIKELY(sz > 4 && sz * 8 < storage.capacity())) {
+ storage.squeeze();
+ }
+ return e;
+ }
+ };
+
public:
QScxmlStateMachinePrivate();
~QScxmlStateMachinePrivate();
- void init();
-
static QScxmlStateMachinePrivate *get(QScxmlStateMachine *t)
{ return t->d_func(); }
- void setQStateMachine(QScxmlInternal::WrappedQStateMachine *stateMachine);
-
- QAbstractState *stateByScxmlName(const QString &scxmlName);
-
ParserData *parserData();
void setIsInvoked(bool invoked)
{ m_isInvoked = invoked; }
- const QVector<QScxmlInvokableService *> &invokedServices() const
- { return m_invokedServices; }
-
- void addService(QScxmlInvokableService *service);
-
- bool removeService(QScxmlInvokableService *service);
+ void addService(int invokingState);
+ void removeService(int invokingState);
+ QScxmlInvokableServiceFactory *serviceFactory(int id);
bool executeInitialSetup();
void routeEvent(QScxmlEvent *event);
void postEvent(QScxmlEvent *event);
+ void submitDelayedEvent(QScxmlEvent *event);
void submitError(const QString &type, const QString &msg, const QString &sendid = QString());
+ void start();
+ void pause();
+ void processEvents();
+
+ void setEvent(QScxmlEvent *event);
+ void resetEvent();
+
+ void emitStateActive(int stateIndex, bool active);
+ void emitServiceChanged(int machineIndex, QScxmlInvokableService *service);
+ void emitSignalForEvent(int signalIndex, const QVariant &data);
+
+private:
+ QStringList stateNames(const std::vector<int> &stateIndexes) const;
+ std::vector<int> historyStates(int stateIdx) const;
+
+ void exitInterpreter();
+ void returnDoneEvent(QScxmlExecutableContent::ContainerId doneData);
+ bool nameMatch(const StateTable::Array &patterns, QScxmlEvent *event) const;
+ void selectTransitions(OrderedSet &enabledTransitions,
+ const std::vector<int> &configInDocumentOrder,
+ QScxmlEvent *event) const;
+ void removeConflictingTransitions(OrderedSet *enabledTransitions) const;
+ void getProperAncestors(std::vector<int> *ancestors, int state1, int state2) const;
+ void microstep(const OrderedSet &enabledTransitions);
+ void exitStates(const OrderedSet &enabledTransitions);
+ void computeExitSet(const OrderedSet &enabledTransitions, OrderedSet &statesToExit) const;
+ void executeTransitionContent(const OrderedSet &enabledTransitions);
+ void enterStates(const OrderedSet &enabledTransitions);
+ void computeEntrySet(const OrderedSet &enabledTransitions,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const;
+ void addDescendantStatesToEnter(int stateIndex,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const;
+ void addAncestorStatesToEnter(int stateIndex,
+ int ancestorIndex,
+ OrderedSet *statesToEnter,
+ OrderedSet *statesForDefaultEntry,
+ HistoryContent *defaultHistoryContent) const;
+ std::vector<int> getChildStates(const StateTable::State &state) const;
+ bool hasDescendant(const OrderedSet &statesToEnter, int childIdx) const;
+ bool allDescendants(const OrderedSet &statesToEnter, int childdx) const;
+ bool isDescendant(int state1, int state2) const;
+ bool allInFinalStates(const std::vector<int> &states) const;
+ bool someInFinalStates(const std::vector<int> &states) const;
+ bool isInFinalState(int stateIndex) const;
+ int getTransitionDomain(int transitionIndex) const;
+ int findLCCA(OrderedSet &&states) const;
+ void getEffectiveTargetStates(OrderedSet *targets, int transitionIndex) const;
+
public: // types & data fields:
QString m_sessionId;
bool m_isInvoked;
bool m_isInitialized;
+ bool m_isProcessingEvents;
QVariantMap m_initialValues;
QScxmlDataModel *m_dataModel;
QScxmlParserPrivate::DefaultLoader m_defaultLoader;
QScxmlParser::Loader *m_loader;
- QScxmlStateMachine::BindingMethod m_dataBinding;
QScxmlExecutableContent::QScxmlExecutionEngine *m_executionEngine;
QScxmlTableData *m_tableData;
- QScxmlEvent m_event;
- QScxmlInternal::WrappedQStateMachine *m_qStateMachine;
- QScxmlEventFilter *m_eventFilter;
- QVector<QScxmlState*> m_statesToInvoke;
+ const StateTable *m_stateTable;
QScxmlStateMachine *m_parentStateMachine;
+ QScxmlInternal::EventLoopHook m_eventLoopHook;
+ typedef std::vector<std::pair<int, QScxmlEvent *>> DelayedQueue;
+ DelayedQueue m_delayedEvents;
private:
- QVector<QScxmlInvokableService *> m_invokedServices;
QScopedPointer<ParserData> m_parserData; // used when created by StateMachine::fromFile.
+ typedef QHash<int, QVector<int>> HistoryValues;
+ struct InvokedService {
+ int invokingState;
+ QScxmlInvokableService *service;
+ QString serviceName;
+ };
+
+ // TODO: move the stuff below to a struct that can be reset
+ HistoryValues m_historyValue;
+ OrderedSet m_configuration;
+ Queue m_internalQueue;
+ Queue m_externalQueue;
+ QSet<int> m_statesToInvoke;
+ std::vector<InvokedService> m_invokedServices;
+ std::vector<bool> m_isFirstStateEntry;
+ std::vector<QScxmlInvokableServiceFactory *> m_cachedFactories;
+ enum { Invalid = 0, Starting, Running, Paused, Finished } m_runningState = Invalid;
+ bool isRunnable() const {
+ switch (m_runningState) {
+ case Starting:
+ case Running:
+ case Paused:
+ return true;
+ case Invalid:
+ case Finished:
+ return false;
+ }
+
+ return false; // Dead code, but many dumb compilers cannot (or are unwilling to) detect that.
+ }
+
+ bool isPaused() const { return m_runningState == Paused; }
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmltabledata.cpp b/src/scxml/qscxmltabledata.cpp
index 75469f9..33f3103 100644
--- a/src/scxml/qscxmltabledata.cpp
+++ b/src/scxml/qscxmltabledata.cpp
@@ -37,11 +37,1019 @@
**
****************************************************************************/
-#include "qscxmltabledata.h"
+#include "qscxmltabledata_p.h"
+#include "qscxmlparser_p.h"
+#include "qscxmlexecutablecontent_p.h"
-QT_BEGIN_NAMESPACE
+QT_USE_NAMESPACE
+
+using namespace QScxmlInternal;
+
+namespace {
+using namespace QScxmlExecutableContent;
+
+class TableDataBuilder: public DocumentModel::NodeVisitor
+{
+public:
+ TableDataBuilder(GeneratedTableData &tableData,
+ GeneratedTableData::MetaDataInfo &metaDataInfo,
+ GeneratedTableData::DataModelInfo &dataModelInfo,
+ GeneratedTableData::CreateFactoryId func)
+ : createFactoryId(func)
+ , m_tableData(tableData)
+ , m_dataModelInfo(dataModelInfo)
+ , m_stringTable(tableData.theStrings)
+ , m_instructions(tableData.theInstructions)
+ , m_evaluators(tableData.theEvaluators)
+ , m_assignments(tableData.theAssignments)
+ , m_foreaches(tableData.theForeaches)
+ , m_dataIds(tableData.theDataNameIds)
+ , m_outgoingEvents(metaDataInfo.outgoingEvents)
+ , m_stateNames(metaDataInfo.stateNames)
+ , m_subStateMachineNames(metaDataInfo.subStateMachineNames)
+ , m_incomingEvents(metaDataInfo.incomingEvents)
+
+ {
+ m_activeSequences.reserve(4);
+ tableData.theInitialSetup = QScxmlExecutableContent::NoInstruction;
+ }
+
+ void buildTableData(DocumentModel::ScxmlDocument *doc)
+ {
+ m_qtMode = doc->qtMode;
+ m_isCppDataModel = doc->root->dataModel == DocumentModel::Scxml::CppDataModel;
+ m_parents.reserve(32);
+ m_allTransitions.resize(doc->allTransitions.size());
+ m_docTransitionIndices.reserve(doc->allTransitions.size());
+ foreach (auto *t, doc->allTransitions) {
+ m_docTransitionIndices.insert(t, m_docTransitionIndices.size());
+ }
+ m_docStatesIndices.reserve(doc->allStates.size());
+ m_transitionsForState.resize(doc->allStates.size());
+ m_allStates.resize(doc->allStates.size());
+ foreach (DocumentModel::AbstractState *s, doc->allStates) {
+ m_docStatesIndices.insert(s, m_docStatesIndices.size());
+ }
+
+ doc->root->accept(this);
+ m_stateTable.version = Q_QSCXMLC_OUTPUT_REVISION;
+ generateStateMachineData();
+
+ m_tableData.theInstructions.squeeze();
+ }
+
+ void generateStateMachineData()
+ {
+ const int tableSize = sizeof(StateTable) / sizeof(qint32);
+ const int stateSize = qint32(sizeof(StateTable::State) / sizeof(qint32));
+ const int transitionSize = qint32(sizeof(StateTable::Transition) / sizeof(qint32));
+
+ m_stateTable.stateOffset = tableSize;
+ m_stateTable.stateCount = m_allStates.size();
+ m_stateTable.transitionOffset = m_stateTable.stateOffset +
+ m_stateTable.stateCount * stateSize;
+ m_stateTable.transitionCount = m_allTransitions.size();
+ m_stateTable.arrayOffset = m_stateTable.transitionOffset +
+ m_stateTable.transitionCount * transitionSize;
+ m_stateTable.arraySize = m_arrays.size();
+
+ const qint32 dataSize = qint32(tableSize)
+ + (m_allStates.size() * stateSize)
+ + (m_allTransitions.size() * transitionSize)
+ + m_arrays.size()
+ + 1;
+ QVector<qint32> data(dataSize, -1);
+ qint32 *ptr = data.data();
+
+ memcpy(ptr, &m_stateTable, sizeof(m_stateTable));
+ ptr += tableSize;
+
+ Q_ASSERT(ptr == data.constData() + m_stateTable.stateOffset);
+ memcpy(ptr, m_allStates.constData(),
+ sizeof(StateTable::State) * size_t(m_allStates.size()));
+ ptr += stateSize * size_t(m_allStates.size());
+
+ Q_ASSERT(ptr == data.constData() + m_stateTable.transitionOffset);
+ memcpy(ptr, m_allTransitions.constData(),
+ sizeof(StateTable::Transition) * size_t(m_allTransitions.size()));
+ ptr += transitionSize * size_t(m_allTransitions.size());
+
+ Q_ASSERT(ptr == data.constData() + m_stateTable.arrayOffset);
+ memcpy(ptr, m_arrays.constData(), sizeof(qint32) * size_t(m_arrays.size()));
+ ptr += m_arrays.size();
+
+ *ptr++ = StateTable::terminator;
+
+ Q_ASSERT(ptr == data.constData() + dataSize);
+
+ m_tableData.theStateMachineTable = data;
+ }
+
+protected: // visitor
+ using NodeVisitor::visit;
+
+ bool visit(DocumentModel::Scxml *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ setName(node->name);
+
+ switch (node->dataModel) {
+ case DocumentModel::Scxml::NullDataModel:
+ m_stateTable.dataModel = StateTable::NullDataModel;
+ break;
+ case DocumentModel::Scxml::JSDataModel:
+ m_stateTable.dataModel = StateTable::EcmaScriptDataModel;
+ break;
+ case DocumentModel::Scxml::CppDataModel:
+ m_stateTable.dataModel = StateTable::CppDataModel;
+ break;
+ default:
+ m_stateTable.dataModel = StateTable::InvalidDataModel;
+ break;
+ }
+
+ switch (node->binding) {
+ case DocumentModel::Scxml::EarlyBinding:
+ m_stateTable.binding = StateTable::EarlyBinding;
+ break;
+ case DocumentModel::Scxml::LateBinding:
+ m_stateTable.binding = StateTable::LateBinding;
+ m_bindLate = true;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ m_stateTable.name = addString(node->name);
+
+ m_parents.append(-1);
+ visit(node->children);
+
+ m_dataElements.append(node->dataElements);
+ if (node->script || !m_dataElements.isEmpty() || !node->initialSetup.isEmpty()) {
+ setInitialSetup(startNewSequence());
+ generate(m_dataElements);
+ if (node->script) {
+ node->script->accept(this);
+ }
+ visit(&node->initialSetup);
+ endSequence();
+ }
+
+ QVector<DocumentModel::AbstractState *> childStates;
+ foreach (DocumentModel::StateOrTransition *sot, node->children) {
+ if (DocumentModel::AbstractState *s = dynamic_cast<DocumentModel::AbstractState *>(sot)) {
+ childStates.append(s);
+ }
+ }
+ addStates(&m_stateTable.childStates, childStates);
+ if (node->initialTransition) {
+ visit(node->initialTransition);
+ const int transitionIndex = m_docTransitionIndices.value(node->initialTransition, -1);
+ Q_ASSERT(transitionIndex != -1);
+ m_stateTable.initialTransition = transitionIndex;
+ }
+ m_parents.removeLast();
+
+ return false;
+ }
+
+ bool visit(DocumentModel::State *state) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ m_stateNames.add(state->id);
+ const int stateIndex = m_docStatesIndices.value(state, -1);
+ Q_ASSERT(stateIndex != -1);
+ StateTable::State &newState = m_allStates[stateIndex];
+ newState.name = addString(state->id);
+ newState.parent = currentParent();
+
+ switch (state->type) {
+ case DocumentModel::State::Normal:
+ newState.type = StateTable::State::Normal;
+ break;
+ case DocumentModel::State::Parallel:
+ newState.type = StateTable::State::Parallel;
+ break;
+ case DocumentModel::State::Final:
+ newState.type = StateTable::State::Final;
+ newState.doneData = generate(state->doneData);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ m_parents.append(stateIndex);
+
+ if (!state->dataElements.isEmpty()) {
+ if (m_bindLate) {
+ newState.initInstructions = startNewSequence();
+ generate(state->dataElements);
+ endSequence();
+ } else {
+ m_dataElements.append(state->dataElements);
+ }
+ }
+
+ newState.entryInstructions = generate(state->onEntry);
+ newState.exitInstructions = generate(state->onExit);
+ if (!state->invokes.isEmpty()) {
+ QVector<int> factoryIds;
+ foreach (DocumentModel::Invoke *invoke, state->invokes) {
+ auto ctxt = createContext(QStringLiteral("invoke"));
+ QVector<QScxmlExecutableContent::StringId> namelist;
+ foreach (const QString &name, invoke->namelist)
+ namelist += addString(name);
+ QVector<QScxmlExecutableContent::Param> params;
+ foreach (DocumentModel::Param *param, invoke->params) {
+ QScxmlExecutableContent::Param p;
+ p.name = addString(param->name);
+ p.expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"),
+ param->expr);
+ p.location = addString(param->location);
+ params.append(p);
+ }
+ QScxmlExecutableContent::ContainerId finalize =
+ QScxmlExecutableContent::NoInstruction;
+ if (!invoke->finalize.isEmpty()) {
+ finalize = startNewSequence();
+ visit(&invoke->finalize);
+ endSequence();
+ }
+ auto srcexpr = createEvaluatorString(QStringLiteral("invoke"),
+ QStringLiteral("srcexpr"),
+ invoke->srcexpr);
+ const int factoryId = createFactoryId(
+ ctxt, srcexpr, addString(invoke->id),
+ addString(state->id + QStringLiteral(".session-")),
+ addString(invoke->idLocation), namelist, invoke->autoforward, params,
+ finalize, invoke->content);
+ Q_ASSERT(factoryId >= 0);
+ factoryIds.append(factoryId);
+ m_stateTable.maxServiceId = std::max(m_stateTable.maxServiceId, factoryId);
+ if (invoke->content && invoke->content->root) {
+ QString name = invoke->content->root->name;
+ if (!name.isEmpty())
+ m_subStateMachineNames.add(name);
+ }
+ }
+ addArray(&newState.serviceFactoryIds, factoryIds);
+ }
+
+ visit(state->children);
+
+ QVector<DocumentModel::AbstractState *> childStates;
+ foreach (DocumentModel::StateOrTransition *sot, state->children) {
+ if (auto s = dynamic_cast<DocumentModel::AbstractState *>(sot)) {
+ childStates.append(s);
+ }
+ }
+ addStates(&newState.childStates, childStates);
+ addArray(&newState.transitions, m_transitionsForState.at(stateIndex));
+ if (state->initialTransition) {
+ visit(state->initialTransition);
+ newState.initialTransition = m_transitionsForState.at(stateIndex).last();
+ }
+ m_parents.removeLast();
+
+ return false;
+ }
+
+ bool visit(DocumentModel::Transition *transition) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ const int transitionIndex = m_docTransitionIndices.value(transition, -1);
+ Q_ASSERT(transitionIndex != -1);
+ StateTable::Transition &newTransition = m_allTransitions[transitionIndex];
+ const int parentIndex = currentParent();
+ if (parentIndex != -1) {
+ m_transitionsForState[parentIndex].append(transitionIndex);
+ }
+ newTransition.source = parentIndex;
+
+ if (transition->condition) {
+ newTransition.condition = createEvaluatorBool(QStringLiteral("transition"),
+ QStringLiteral("cond"),
+ *transition->condition.data());
+ }
+
+ switch (transition->type) {
+ case DocumentModel::Transition::External:
+ newTransition.type = StateTable::Transition::External;
+ break;
+ case DocumentModel::Transition::Internal:
+ newTransition.type = StateTable::Transition::Internal;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ if (!transition->instructionsOnTransition.isEmpty()) {
+ m_currentTransition = transitionIndex;
+ newTransition.transitionInstructions = startNewSequence();
+ visit(&transition->instructionsOnTransition);
+ endSequence();
+ m_currentTransition = -1;
+ }
+
+ addStates(&newTransition.targets, transition->targetStates);
+
+ QVector<int> eventIds;
+ foreach (const QString &event, transition->events) {
+ eventIds.push_back(addString(event));
+ if (m_qtMode) {
+ m_incomingEvents.add(event);
+ }
+ }
+ addArray(&newTransition.events, eventIds);
+
+ return false;
+ }
+
+ bool visit(DocumentModel::HistoryState *historyState) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ const int stateIndex = m_docStatesIndices.value(historyState, -1);
+ Q_ASSERT(stateIndex != -1);
+ StateTable::State &newState = m_allStates[stateIndex];
+ newState.name = addString(historyState->id);
+ newState.parent = currentParent();
+
+ switch (historyState->type) {
+ case DocumentModel::HistoryState::Shallow:
+ newState.type = StateTable::State::ShallowHistory;
+ break;
+ case DocumentModel::HistoryState::Deep:
+ newState.type = StateTable::State::DeepHistory;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ m_parents.append(stateIndex);
+ visit(historyState->children);
+ m_parents.removeLast();
+ addArray(&newState.transitions, m_transitionsForState.at(stateIndex));
+ return false;
+ }
+
+ bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ if (m_qtMode && node->type == QStringLiteral("qt:signal")) {
+ m_outgoingEvents.add(m_stringTable.item(addString(node->event)));
+ }
+
+ auto instr = m_instructions.add<Send>(Send::calculateExtraSize(node->params.size(),
+ node->namelist.size()));
+ instr->instructionLocation = createContext(QStringLiteral("send"));
+ instr->event = addString(node->event);
+ instr->eventexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("eventexpr"),
+ node->eventexpr);
+ instr->type = addString(node->type);
+ instr->typeexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("typeexpr"),
+ node->typeexpr);
+ instr->target = addString(node->target);
+ instr->targetexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("targetexpr"),
+ node->targetexpr);
+ instr->id = addString(node->id);
+ instr->idLocation = addString(node->idLocation);
+ instr->delay = addString(node->delay);
+ instr->delayexpr = createEvaluatorString(QStringLiteral("send"),
+ QStringLiteral("delayexpr"),
+ node->delayexpr);
+ instr->content = addString(node->content);
+ generate(&instr->namelist, node->namelist);
+ generate(instr->params(), node->params);
+ return false;
+ }
+
+ void visit(DocumentModel::Raise *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Raise>();
+ instr->event = addString(node->event);
+ }
+
+ void visit(DocumentModel::Log *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Log>();
+ instr->label = addString(node->label);
+ instr->expr = createEvaluatorString(QStringLiteral("log"),
+ QStringLiteral("expr"),
+ node->expr);
+ }
+
+ void visit(DocumentModel::Script *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<JavaScript>();
+ instr->go = createEvaluatorVoid(QStringLiteral("script"),
+ QStringLiteral("source"),
+ node->content);
+ }
+
+ void visit(DocumentModel::Assign *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Assign>();
+ auto ctxt = createContext(QStringLiteral("assign"), QStringLiteral("expr"), node->expr);
+ instr->expression = addAssignment(node->location, node->expr, ctxt);
+ }
+
+ bool visit(DocumentModel::If *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<If>(node->conditions.size());
+ instr->conditions.count = node->conditions.size();
+ auto it = instr->conditions.data();
+ QString tag = QStringLiteral("if");
+ for (int i = 0, ei = node->conditions.size(); i != ei; ++i) {
+ *it++ = createEvaluatorBool(tag, QStringLiteral("cond"), node->conditions.at(i));
+ if (i == 0) {
+ tag = QStringLiteral("elif");
+ }
+ }
+ auto outSequences = m_instructions.add<InstructionSequences>();
+ generate(outSequences, node->blocks);
+ return false;
+ }
+
+ bool visit(DocumentModel::Foreach *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Foreach>();
+ auto ctxt = createContextString(QStringLiteral("foreach"));
+ instr->doIt = addForeach(node->array, node->item, node->index, ctxt);
+ startSequence(&instr->block);
+ visit(&node->block);
+ endSequence();
+ return false;
+ }
+
+ void visit(DocumentModel::Cancel *node) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto instr = m_instructions.add<Cancel>();
+ instr->sendid = addString(node->sendid);
+ instr->sendidexpr = createEvaluatorString(QStringLiteral("cancel"),
+ QStringLiteral("sendidexpr"),
+ node->sendidexpr);
+ }
+
+protected:
+ ContainerId generate(const DocumentModel::DoneData *node)
+ {
+ auto id = m_instructions.newContainerId();
+ DoneData *doneData;
+ if (node) {
+ doneData = m_instructions.add<DoneData>(node->params.size() * Param::calculateSize());
+ doneData->contents = addString(node->contents);
+ doneData->expr = createEvaluatorString(QStringLiteral("donedata"),
+ QStringLiteral("expr"),
+ node->expr);
+ generate(&doneData->params, node->params);
+ } else {
+ doneData = m_instructions.add<DoneData>();
+ doneData->contents = NoString;
+ doneData->expr = NoEvaluator;
+ doneData->params.count = 0;
+ }
+ doneData->location = createContext(QStringLiteral("final"));
+ return id;
+ }
+
+ StringId createContext(const QString &instrName)
+ {
+ return addString(createContextString(instrName));
+ }
+
+ void generate(const QVector<DocumentModel::DataElement *> &dataElements)
+ {
+ foreach (DocumentModel::DataElement *el, dataElements) {
+ auto ctxt = createContext(QStringLiteral("data"), QStringLiteral("expr"), el->expr);
+ auto evaluator = addDataElement(el->id, el->expr, ctxt);
+ if (evaluator != NoEvaluator) {
+ auto instr = m_instructions.add<QScxmlExecutableContent::Initialize>();
+ instr->expression = evaluator;
+ }
+ }
+ }
+
+ ContainerId generate(const DocumentModel::InstructionSequences &inSequences)
+ {
+ if (inSequences.isEmpty())
+ return NoInstruction;
+
+ auto id = m_instructions.newContainerId();
+ auto outSequences = m_instructions.add<InstructionSequences>();
+ generate(outSequences, inSequences);
+ return id;
+ }
+
+ void generate(Array<Param> *out, const QVector<DocumentModel::Param *> &in)
+ {
+ out->count = in.size();
+ Param *it = out->data();
+ foreach (DocumentModel::Param *f, in) {
+ it->name = addString(f->name);
+ it->expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"),
+ f->expr);
+ it->location = addString(f->location);
+ ++it;
+ }
+ }
+
+ void generate(InstructionSequences *outSequences,
+ const DocumentModel::InstructionSequences &inSequences)
+ {
+ int sequencesOffset = m_instructions.offset(outSequences);
+ int sequenceCount = 0;
+ int entryCount = 0;
+ foreach (DocumentModel::InstructionSequence *sequence, inSequences) {
+ ++sequenceCount;
+ startNewSequence();
+ visit(sequence);
+ entryCount += endSequence()->size();
+ }
+ outSequences = m_instructions.at<InstructionSequences>(sequencesOffset);
+ outSequences->sequenceCount = sequenceCount;
+ outSequences->entryCount = entryCount;
+ }
+
+ void generate(Array<StringId> *out, const QStringList &in)
+ {
+ out->count = in.size();
+ StringId *it = out->data();
+ foreach (const QString &str, in) {
+ *it++ = addString(str);
+ }
+ }
+
+ ContainerId startNewSequence()
+ {
+ auto id = m_instructions.newContainerId();
+ auto sequence = m_instructions.add<InstructionSequence>();
+ startSequence(sequence);
+ return id;
+ }
+
+ void startSequence(InstructionSequence *sequence)
+ {
+ SequenceInfo info;
+ info.location = m_instructions.offset(sequence);
+ info.entryCount = 0;
+ m_activeSequences.push_back(info);
+ m_instructions.setSequenceInfo(&m_activeSequences.last());
+ sequence->instructionType = Instruction::Sequence;
+ sequence->entryCount = -1; // checked in endSequence
+ }
+
+ InstructionSequence *endSequence()
+ {
+ SequenceInfo info = m_activeSequences.back();
+ m_activeSequences.pop_back();
+ m_instructions.setSequenceInfo(m_activeSequences.isEmpty() ? Q_NULLPTR :
+ &m_activeSequences.last());
+
+ auto sequence = m_instructions.at<InstructionSequence>(info.location);
+ Q_ASSERT(sequence->entryCount == -1); // set in startSequence
+ sequence->entryCount = info.entryCount;
+ if (!m_activeSequences.isEmpty())
+ m_activeSequences.last().entryCount += info.entryCount;
+ return sequence;
+ }
+
+ EvaluatorId createEvaluatorString(const QString &instrName, const QString &attrName,
+ const QString &expr)
+ {
+ if (!expr.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.stringEvaluators.insert(id, expr);
+ return id;
+ } else {
+ return addEvaluator(expr, createContext(instrName, attrName, expr));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ EvaluatorId createEvaluatorBool(const QString &instrName, const QString &attrName,
+ const QString &cond)
+ {
+ if (!cond.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.boolEvaluators.insert(id, cond);
+ return id;
+ } else {
+ return addEvaluator(cond, createContext(instrName, attrName, cond));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ EvaluatorId createEvaluatorVariant(const QString &instrName, const QString &attrName,
+ const QString &expr)
+ {
+ if (!expr.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.variantEvaluators.insert(id, expr);
+ return id;
+ } else {
+ return addEvaluator(expr, createContext(instrName, attrName, expr));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ EvaluatorId createEvaluatorVoid(const QString &instrName, const QString &attrName,
+ const QString &stuff)
+ {
+ if (!stuff.isEmpty()) {
+ if (isCppDataModel()) {
+ auto id = m_evaluators.add(EvaluatorInfo(), false);
+ m_dataModelInfo.voidEvaluators.insert(id, stuff);
+ return id;
+ } else {
+ return addEvaluator(stuff, createContext(instrName, attrName, stuff));
+ }
+ }
+
+ return NoEvaluator;
+ }
+
+ GeneratedTableData *tableData(const QVector<int> &stateMachineTable);
+
+ StringId addString(const QString &str)
+ { return str.isEmpty() ? NoString : m_stringTable.add(str); }
+
+ void setInitialSetup(ContainerId id)
+ { m_tableData.theInitialSetup = id; }
+
+ void setName(const QString &name)
+ { m_tableData.theName = addString(name); }
+
+ bool isCppDataModel() const
+ { return m_isCppDataModel; }
+
+ void addStates(int *idx, const QVector<DocumentModel::AbstractState *> &states)
+ {
+ QVector<int> array;
+ foreach (auto *s, states) {
+ int si = m_docStatesIndices.value(s, -1);
+ Q_ASSERT(si != -1);
+ array.push_back(si);
+ }
+
+ addArray(idx, array);
+ }
+
+ void addArray(int *idx, const QVector<int> &array)
+ {
+ if (array.isEmpty()) {
+ *idx = -1;
+ return;
+ }
+
+ *idx = m_arrays.size();
+ m_arrays.push_back(array.size());
+ m_arrays.append(array);
+ }
+
+ int currentParent() const
+ {
+ return m_parents.last();
+ }
+
+ QString createContextString(const QString &instrName) const
+ {
+ if (m_currentTransition != -1) {
+ QString state;
+ int parent = m_allTransitions.at(m_currentTransition).source;
+ if (parent != -1) {
+ QString parentName = QStringLiteral("(none)");
+ int name = m_allStates.at(parent).name;
+ if (name != -1) {
+ parentName = m_stringTable.item(name);
+ }
+ state = QStringLiteral(" of state '%1'").arg(parentName);
+ }
+ return QStringLiteral("%1 instruction in transition %3").arg(instrName, state);
+ } else {
+ QString parentName = QStringLiteral("(none)");
+ const int parent = currentParent();
+ if (parent != -1) {
+ const int name = m_allStates.at(parent).name;
+ if (name != -1) {
+ parentName = m_stringTable.item(name);
+ }
+ }
+ return QStringLiteral("%1 instruction in state %2").arg(instrName, parentName);
+ }
+ }
+
+ QString createContext(const QString &instrName, const QString &attrName,
+ const QString &attrValue) const
+ {
+ const QString location = createContextString(instrName);
+ return QStringLiteral("%1 with %2=\"%3\"").arg(location, attrName, attrValue);
+ }
+
+ EvaluatorId addEvaluator(const QString &expr, const QString &context)
+ {
+ EvaluatorInfo ei;
+ ei.expr = addString(expr);
+ ei.context = addString(context);
+ return m_evaluators.add(ei);
+ }
+
+ EvaluatorId addAssignment(const QString &dest, const QString &expr, const QString &context)
+ {
+ AssignmentInfo ai;
+ ai.dest = addString(dest);
+ ai.expr = addString(expr);
+ ai.context = addString(context);
+ return m_assignments.add(ai);
+ }
+
+ EvaluatorId addForeach(const QString &array, const QString &item, const QString &index,
+ const QString &context)
+ {
+ ForeachInfo fi;
+ fi.array = addString(array);
+ fi.item = addString(item);
+ fi.index = addString(index);
+ fi.context = addString(context);
+ return m_foreaches.add(fi);
+ }
+
+ EvaluatorId addDataElement(const QString &id, const QString &expr, const QString &context)
+ {
+ auto str = addString(id);
+ if (!m_dataIds.contains(str))
+ m_dataIds.append(str);
+ if (expr.isEmpty())
+ return NoEvaluator;
+
+ return addAssignment(id, expr, context);
+ }
+
+private:
+ template <class Container, typename T, typename U>
+ class Table {
+ Container &elements;
+ QMap<T, int> indexForElement;
+
+ public:
+ Table(Container &storage)
+ : elements(storage)
+ {}
+
+ U add(const T &s, bool uniqueOnly = true) {
+ int pos = uniqueOnly ? indexForElement.value(s, -1) : -1;
+ if (pos == -1) {
+ pos = elements.size();
+ elements.append(s);
+ indexForElement.insert(s, pos);
+ }
+ return pos;
+ }
+
+ Container data() {
+ return elements;
+ }
+
+ const T &item(U pos) const {
+ return elements.at(pos);
+ }
+ };
+
+ struct SequenceInfo {
+ int location;
+ qint32 entryCount; // the amount of qint32's that the instructions take up
+ };
+
+ class InstructionStorage {
+ public:
+ InstructionStorage(QVector<qint32> &storage)
+ : m_instr(storage)
+ , m_info(Q_NULLPTR)
+ {}
+
+ ContainerId newContainerId() const { return m_instr.size(); }
+
+ template <typename T>
+ T *add(int extra = 0)
+ {
+ int pos = m_instr.size();
+ int size = sizeof(T) / sizeof(qint32) + extra;
+ if (m_info)
+ m_info->entryCount += size;
+ m_instr.resize(pos + size);
+ T *instr = at<T>(pos);
+ Q_ASSERT(instr->instructionType == 0);
+ instr->instructionType = T::kind();
+ return instr;
+ }
+
+ int offset(Instruction *instr) const
+ {
+ return reinterpret_cast<qint32 *>(instr) - m_instr.data();
+ }
+
+ template <typename T>
+ T *at(int offset)
+ {
+ return reinterpret_cast<T *>(&m_instr[offset]);
+ }
+
+ void setSequenceInfo(SequenceInfo *info)
+ {
+ m_info = info;
+ }
+
+ private:
+ QVector<qint32> &m_instr;
+ SequenceInfo *m_info;
+ };
+
+ QVector<SequenceInfo> m_activeSequences;
+
+ GeneratedTableData::CreateFactoryId createFactoryId;
+ GeneratedTableData &m_tableData;
+ GeneratedTableData::DataModelInfo &m_dataModelInfo;
+ Table<QStringList, QString, StringId> m_stringTable;
+ InstructionStorage m_instructions;
+ Table<QVector<EvaluatorInfo>, EvaluatorInfo, EvaluatorId> m_evaluators;
+ Table<QVector<AssignmentInfo>, AssignmentInfo, EvaluatorId> m_assignments;
+ Table<QVector<ForeachInfo>, ForeachInfo, EvaluatorId> m_foreaches;
+ StringIds &m_dataIds;
+ bool m_isCppDataModel = false;
+
+ StateTable m_stateTable;
+ QVector<int> m_parents;
+ QVector<qint32> m_arrays;
+
+ QVector<StateTable::Transition> m_allTransitions;
+ QHash<DocumentModel::Transition *, int> m_docTransitionIndices;
+ QVector<StateTable::State> m_allStates;
+ QHash<DocumentModel::AbstractState *, int> m_docStatesIndices;
+ QVector<QVector<int>> m_transitionsForState;
+
+ int m_currentTransition = StateTable::InvalidIndex;
+ bool m_bindLate = false;
+ bool m_qtMode = false;
+ QVector<DocumentModel::DataElement *> m_dataElements;
+ Table<QStringList, QString, int> m_outgoingEvents;
+ Table<QStringList, QString, int> m_stateNames;
+ Table<QStringList, QString, int> m_subStateMachineNames;
+ Table<QStringList, QString, int> m_incomingEvents;
+};
+
+} // anonymous namespace
QScxmlTableData::~QScxmlTableData()
{}
-QT_END_NAMESPACE
+void GeneratedTableData::build(DocumentModel::ScxmlDocument *doc,
+ GeneratedTableData *table,
+ MetaDataInfo *metaDataInfo,
+ DataModelInfo *dataModelInfo,
+ GeneratedTableData::CreateFactoryId func)
+{
+ TableDataBuilder builder(*table, *metaDataInfo, *dataModelInfo, func);
+ builder.buildTableData(doc);
+}
+
+QString GeneratedTableData::toString(const int *stateMachineTable)
+{
+ QString result;
+ QTextStream out(&result);
+
+ const StateTable *st = reinterpret_cast<const StateTable *>(stateMachineTable);
+
+ out << "{" << endl
+ << "\t0x" << hex << st->version << dec << ", // version" << endl
+ << "\t" << st->name << ", // name" << endl
+ << "\t" << st->dataModel << ", // data-model" << endl
+ << "\t" << st->childStates << ", // child states array offset" << endl
+ << "\t" << st->initialTransition << ", // transition to initial states" << endl
+ << "\t" << st->initialSetup << ", // initial setup" << endl
+ << "\t" << st->binding << ", // binding" << endl
+ << "\t" << st->maxServiceId << ", // maxServiceId" << endl
+ << "\t" << st->stateOffset << ", " << st->stateCount
+ << ", // state offset and count" << endl
+ << "\t" << st->transitionOffset << ", " << st->transitionCount
+ << ", // transition offset and count" << endl
+ << "\t" << st->arrayOffset << ", " << st->arraySize << ", // array offset and size" << endl
+ << endl;
+
+ out << "\t// States:" << endl;
+ for (int i = 0; i < st->stateCount; ++i) {
+ const StateTable::State &s = st->state(i);
+ out << "\t"
+ << s.name << ", "
+ << s.parent << ", "
+ << s.type << ", "
+ << s.initialTransition << ", "
+ << s.initInstructions << ", "
+ << s.entryInstructions << ", "
+ << s.exitInstructions << ", "
+ << s.doneData << ", "
+ << s.childStates << ", "
+ << s.transitions << ", "
+ << s.serviceFactoryIds << ","
+ << endl;
+ }
+
+ out << endl
+ << "\t// Transitions:" << endl;
+ for (int i = 0; i < st->transitionCount; ++i) {
+ auto t = st->transition(i);
+ out << "\t"
+ << t.events << ", "
+ << t.condition << ", "
+ << t.type << ", "
+ << t.source << ", "
+ << t.targets << ", "
+ << t.transitionInstructions << ", "
+ << endl ;
+ }
+
+ out << endl
+ << "\t// Arrays:" << endl;
+ int nextStart = 0;
+ while (nextStart < st->arraySize) {
+ const StateTable::Array a = st->array(nextStart);
+ out << "\t" << a.size() << ", ";
+ for (int j = 0; j < a.size(); ++j) {
+ out << a[j] << ", ";
+ }
+ out << endl;
+ nextStart += a.size() + 1;
+ }
+
+ out << hex;
+ out << endl
+ << "\t0x" << StateTable::terminator << " // terminator" << endl
+ << "}";
+
+ return result;
+}
+
+QString GeneratedTableData::string(StringId id) const
+{
+ return id == NoString ? QString() : theStrings.at(id);
+}
+
+Instructions GeneratedTableData::instructions() const
+{
+ return const_cast<Instructions>(theInstructions.data());
+}
+
+EvaluatorInfo GeneratedTableData::evaluatorInfo(EvaluatorId evaluatorId) const
+{
+ return theEvaluators[evaluatorId];
+}
+
+AssignmentInfo GeneratedTableData::assignmentInfo(EvaluatorId assignmentId) const
+{
+ return theAssignments[assignmentId];
+}
+
+ForeachInfo GeneratedTableData::foreachInfo(EvaluatorId foreachId) const
+{
+ return theForeaches[foreachId];
+}
+
+StringId *GeneratedTableData::dataNames(int *count) const
+{
+ Q_ASSERT(count);
+ *count = theDataNameIds.size();
+ return const_cast<StringId *>(theDataNameIds.data());
+}
+
+ContainerId GeneratedTableData::initialSetup() const
+{
+ return theInitialSetup;
+}
+
+QString GeneratedTableData::name() const
+{
+ return string(theName);
+}
+
+const qint32 *GeneratedTableData::stateMachineTable() const
+{
+ return theStateMachineTable.constData();
+}
+
+QScxmlInvokableServiceFactory *GeneratedTableData::serviceFactory(int id) const
+{
+ Q_UNUSED(id);
+ return nullptr;
+}
+
+int GeneratedTableData::signalIndexForEvent(const QString &event) const
+{
+ Q_UNUSED(event);
+ return -1;
+}
diff --git a/src/scxml/qscxmltabledata.h b/src/scxml/qscxmltabledata.h
index 59fb105..9f025bf 100644
--- a/src/scxml/qscxmltabledata.h
+++ b/src/scxml/qscxmltabledata.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSCXMLTABLEDATA_P
-#define QSCXMLTABLEDATA_P
+#ifndef QSCXMLTABLEDATA_H
+#define QSCXMLTABLEDATA_H
#include <QtScxml/qscxmlexecutablecontent.h>
#include <QString>
@@ -49,6 +49,8 @@
QT_BEGIN_NAMESPACE
+class QScxmlInvokableServiceFactory;
+
class Q_SCXML_EXPORT QScxmlTableData
{
public:
@@ -63,8 +65,12 @@ public:
virtual QScxmlExecutableContent::ContainerId initialSetup() const = 0;
virtual QString name() const = 0;
+
+ virtual const qint32 *stateMachineTable() const = 0;
+ virtual QScxmlInvokableServiceFactory *serviceFactory(int id) const = 0;
+ virtual int signalIndexForEvent(const QString &event) const = 0;
};
QT_END_NAMESPACE
-#endif // QSCXMLTABLEDATA_P
+#endif // QSCXMLTABLEDATA_H
diff --git a/src/scxml/qscxmltabledata_p.h b/src/scxml/qscxmltabledata_p.h
new file mode 100644
index 0000000..85d3145
--- /dev/null
+++ b/src/scxml/qscxmltabledata_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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 QSCXMLTABLEDATA_P_H
+#define QSCXMLTABLEDATA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtScxml/qscxmltabledata.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qhash.h>
+
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+class QScxmlInvokableServiceFactory;
+
+namespace DocumentModel {
+struct ScxmlDocument;
+}
+
+namespace QScxmlInternal {
+class Q_SCXML_EXPORT GeneratedTableData: public QScxmlTableData
+{
+public:
+ typedef std::function<
+ int(QScxmlExecutableContent::StringId invokeLocation,
+ QScxmlExecutableContent::EvaluatorId srcexpr,
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId idPrefix,
+ QScxmlExecutableContent::StringId idlocation,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ bool autoforward,
+ const QVector<QScxmlExecutableContent::Param> &params,
+ QScxmlExecutableContent::ContainerId finalize,
+ QSharedPointer<DocumentModel::ScxmlDocument> content)
+ > CreateFactoryId;
+
+ struct MetaDataInfo {
+ QStringList outgoingEvents;
+ QStringList stateNames;
+ QStringList subStateMachineNames;
+ QStringList incomingEvents;
+ };
+
+ struct DataModelInfo {
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> stringEvaluators;
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> boolEvaluators;
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> variantEvaluators;
+ QHash<QScxmlExecutableContent::EvaluatorId, QString> voidEvaluators;
+ };
+
+public:
+ static void build(DocumentModel::ScxmlDocument *doc, GeneratedTableData *table,
+ MetaDataInfo *metaDataInfo, DataModelInfo *dataModelInfo,
+ CreateFactoryId func);
+ static QString toString(const int *stateMachineTable);
+
+public:
+ QString string(QScxmlExecutableContent::StringId id) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::Instructions instructions() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::EvaluatorInfo evaluatorInfo(
+ QScxmlExecutableContent::EvaluatorId evaluatorId) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::AssignmentInfo assignmentInfo(
+ QScxmlExecutableContent::EvaluatorId assignmentId) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::ForeachInfo foreachInfo(
+ QScxmlExecutableContent::EvaluatorId foreachId) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::StringId *dataNames(int *count) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ const qint32 *stateMachineTable() const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ QScxmlInvokableServiceFactory *serviceFactory(int id) const Q_DECL_OVERRIDE;
+ int signalIndexForEvent(const QString &event) const Q_DECL_OVERRIDE;
+
+public:
+ QVector<qint32> theStateMachineTable;
+ QStringList theStrings;
+ QVector<qint32> theInstructions;
+ QVector<QScxmlExecutableContent::EvaluatorInfo> theEvaluators;
+ QVector<QScxmlExecutableContent::AssignmentInfo> theAssignments;
+ QVector<QScxmlExecutableContent::ForeachInfo> theForeaches;
+ QScxmlExecutableContent::StringIds theDataNameIds;
+ QScxmlExecutableContent::EvaluatorId theInitialSetup;
+ int theName;
+};
+} // QScxmlInternal namespace
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLTABLEDATA_P_H
diff --git a/src/scxml/scxml.pro b/src/scxml/scxml.pro
index 40601e3..4453a83 100644
--- a/src/scxml/scxml.pro
+++ b/src/scxml/scxml.pro
@@ -25,13 +25,12 @@ HEADERS += \
qscxmlevent_p.h \
qscxmldatamodel.h \
qscxmldatamodel_p.h \
- qscxmlqstates.h \
- qscxmlqstates_p.h \
qscxmlcppdatamodel_p.h \
qscxmlcppdatamodel.h \
qscxmlerror.h \
qscxmlinvokableservice.h \
- qscxmltabledata.h
+ qscxmltabledata.h \
+ qscxmltabledata_p.h
SOURCES += \
qscxmlparser.cpp \
@@ -42,7 +41,6 @@ SOURCES += \
qscxmlexecutablecontent.cpp \
qscxmlevent.cpp \
qscxmldatamodel.cpp \
- qscxmlqstates.cpp \
qscxmlcppdatamodel.cpp \
qscxmlerror.cpp \
qscxmlinvokableservice.cpp \
diff --git a/tests/auto/compiled/tst_compiled.cpp b/tests/auto/compiled/tst_compiled.cpp
index 49174e3..c6ee894 100644
--- a/tests/auto/compiled/tst_compiled.cpp
+++ b/tests/auto/compiled/tst_compiled.cpp
@@ -54,15 +54,16 @@ void tst_Compiled::stateNames()
{
ids1 stateMachine;
+ // The states have to be appear in document order:
QStringList ids1States({
- "_",
- "_VALID",
- "__valid",
- "foo-bar",
"foo.bar",
+ "foo-bar",
"foo_bar",
- "n_0xe4_l",
+ "_",
"näl",
+ "n_0xe4_l",
+ "_VALID",
+ "__valid",
"qÿ̀i",
});
diff --git a/tests/auto/scion/tst_scion.cpp b/tests/auto/scion/tst_scion.cpp
index c06ac1b..5b7bded 100644
--- a/tests/auto/scion/tst_scion.cpp
+++ b/tests/auto/scion/tst_scion.cpp
@@ -55,8 +55,6 @@ static QSet<QString> testFailOnRun = QSet<QString>()
<< QLatin1String("w3c-ecma/test178.txml")
// We do not support the optional basic http event i/o processor.
<< QLatin1String("w3c-ecma/test201.txml")
- << QLatin1String("w3c-ecma/test364.txml") // initial attribute on <state>
- << QLatin1String("w3c-ecma/test388.txml") // Qt refuses to set an initial state to a "deep" state
<< QLatin1String("w3c-ecma/test230.txml")
<< QLatin1String("w3c-ecma/test250.txml")
<< QLatin1String("w3c-ecma/test307.txml")
@@ -387,7 +385,10 @@ bool TestScion::runTest(QScxmlStateMachine *stateMachine, const QJsonObject &tes
return playEvents(stateMachine, testDescription);
} else {
// Wait for all events (delayed or otherwise) to propagate.
- finishedSpy.fastWait(); // Some tests don't have a final state, so don't check for the result.
+ if (stateMachine->isRunning()) {
+ finishedSpy.fastWait(); // Some tests don't have a final state, so don't check for the
+ // result
+ }
return verifyStates(stateMachine, testDescription, QLatin1String("initialConfiguration"), 0);
}
}
diff --git a/tests/auto/statemachine/eventoccurred.scxml b/tests/auto/statemachine/eventoccurred.scxml
index 87aaf41..ffceb3f 100644
--- a/tests/auto/statemachine/eventoccurred.scxml
+++ b/tests/auto/statemachine/eventoccurred.scxml
@@ -41,13 +41,13 @@
<transition event="timeout" target="final"/>
</state>
<final id="final"/>
- </state>
- <!--
- The done.state.* events are internal, so expose them to the spy too by re-sending them as
- external events:
- -->
- <transition event="done.state.*" cond="_event.type === 'internal'">
- <send eventexpr="_event.name"/>
- </transition>
+ <!--
+ The done.state.* events are internal, so expose them to the spy too by re-sending them as
+ external events:
+ -->
+ <transition event="done.state.*" cond="_event.type === 'internal'">
+ <send eventexpr="_event.name"/>
+ </transition>
+ </state>
</scxml>
diff --git a/tests/auto/statemachine/tst_statemachine.cpp b/tests/auto/statemachine/tst_statemachine.cpp
index fc20118..e2e0d22 100644
--- a/tests/auto/statemachine/tst_statemachine.cpp
+++ b/tests/auto/statemachine/tst_statemachine.cpp
@@ -62,18 +62,18 @@ void tst_StateMachine::stateNames_data()
<< (QStringList() << QString("a1") << QString("a2") << QString("final"));
QTest::newRow("stateNames-notCompressed") << QString(":/tst_statemachine/statenames.scxml")
<< false
- << (QStringList() << QString("a") << QString("a1") << QString("a2") << QString("b") << QString("final") << QString("top"));
+ << (QStringList() << QString("top") << QString("a") << QString("a1") << QString("a2") << QString("b") << QString("final"));
QTest::newRow("stateNamesNested-compressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< true
<< (QStringList() << QString("a") << QString("b"));
QTest::newRow("stateNamesNested-notCompressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< false
- << (QStringList() << QString("a") << QString("b") << QString("super_top"));
+ << (QStringList() << QString("super_top") << QString("a") << QString("b"));
QTest::newRow("ids1") << QString(":/tst_statemachine/ids1.scxml")
<< false
- << (QStringList() << QString("_") << QString("foo-bar")
- << QString("foo.bar") << QString("foo_bar"));
+ << (QStringList() << QString("foo.bar") << QString("foo-bar")
+ << QString("foo_bar") << QString("_"));
}
void tst_StateMachine::stateNames()
@@ -100,13 +100,13 @@ void tst_StateMachine::activeStateNames_data()
<< (QStringList() << QString("a1") << QString("final"));
QTest::newRow("stateNames-notCompressed") << QString(":/tst_statemachine/statenames.scxml")
<< false
- << (QStringList() << QString("a") << QString("a1") << QString("b") << QString("final") << QString("top"));
+ << (QStringList() << QString("top") << QString("a") << QString("a1") << QString("b") << QString("final"));
QTest::newRow("stateNamesNested-compressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< true
- << (QStringList() << QString("a")<< QString("b"));
+ << (QStringList() << QString("a") << QString("b"));
QTest::newRow("stateNamesNested-notCompressed") << QString(":/tst_statemachine/statenamesnested.scxml")
<< false
- << (QStringList() << QString("a") << QString("b") << QString("super_top"));
+ << (QStringList() << QString("super_top") << QString("a") << QString("b"));
}
void tst_StateMachine::activeStateNames()
@@ -150,12 +150,19 @@ void tst_StateMachine::eventOccurred()
finishedSpy.wait(5000);
- QCOMPARE(eventOccurredSpy.count(), 4);
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(0).at(0)).name(), QLatin1String("internalEvent2"));
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(1).at(0)).name(), QLatin1String("externalEvent"));
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(2).at(0)).name(), QLatin1String("timeout"));
- QCOMPARE(qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(3).at(0)).name(), QLatin1String("done.state.top"));
+ auto event = [&eventOccurredSpy](int eventIndex) -> QScxmlEvent {
+ return qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(eventIndex).at(0));
+ };
+ QCOMPARE(eventOccurredSpy.count(), 4);
+ QCOMPARE(event(0).name(), QLatin1String("internalEvent2"));
+ QCOMPARE(event(0).eventType(), QScxmlEvent::ExternalEvent);
+ QCOMPARE(event(1).name(), QLatin1String("externalEvent"));
+ QCOMPARE(event(1).eventType(), QScxmlEvent::ExternalEvent);
+ QCOMPARE(event(2).name(), QLatin1String("timeout"));
+ QCOMPARE(event(2).eventType(), QScxmlEvent::ExternalEvent);
+ QCOMPARE(event(3).name(), QLatin1String("done.state.top"));
+ QCOMPARE(event(3).eventType(), QScxmlEvent::ExternalEvent);
QCOMPARE(externalEventOccurredSpy.count(), 1);
QCOMPARE(qvariant_cast<QScxmlEvent>(externalEventOccurredSpy.at(0).at(0)).name(), QLatin1String("externalEvent"));
diff --git a/tools/qscxmlc/cppdatamodel.t b/tools/qscxmlc/cppdatamodel.t
new file mode 100644
index 0000000..524571b
--- /dev/null
+++ b/tools/qscxmlc/cppdatamodel.t
@@ -0,0 +1,42 @@
+QString ${datamodel}::evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+ switch (id) {
+${evaluateToStringCases} default:
+ Q_UNREACHABLE();
+ *ok = false;
+ return QString();
+ }
+}
+
+bool ${datamodel}::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+ switch (id) {
+${evaluateToBoolCases} default:
+ Q_UNREACHABLE();
+ *ok = false;
+ return false;
+ }
+}
+
+QVariant ${datamodel}::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+ switch (id) {
+${evaluateToVariantCases} default:
+ Q_UNREACHABLE();
+ *ok = false;
+ return QVariant();
+ }
+}
+
+void ${datamodel}::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok)
+{
+ *ok = true;
+ switch (id) {
+${evaluateToVoidCases} default:
+ Q_UNREACHABLE();
+ *ok = false;
+ }
+}
diff --git a/tools/qscxmlc/data.t b/tools/qscxmlc/data.t
new file mode 100644
index 0000000..a1eece5
--- /dev/null
+++ b/tools/qscxmlc/data.t
@@ -0,0 +1,125 @@
+struct ${classname}::Data: private QScxmlTableData {
+ Data(${classname} &stateMachine)
+ : stateMachine(stateMachine)
+ {}
+
+ void init() {
+ stateMachine.setTableData(this);
+ ${dataModelInitialization}
+ }
+
+ QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return ${name}; }
+
+ QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return ${initialSetup}; }
+
+ QScxmlExecutableContent::Instructions instructions() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return theInstructions; }
+
+ QScxmlExecutableContent::StringId *dataNames(int *count) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { *count = ${dataNameCount}; return dataIds; }
+
+ QScxmlExecutableContent::EvaluatorInfo evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { Q_ASSERT(evaluatorId >= 0); Q_ASSERT(evaluatorId < ${evaluatorCount}); return evaluators[evaluatorId]; }
+
+ QScxmlExecutableContent::AssignmentInfo assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { Q_ASSERT(assignmentId >= 0); Q_ASSERT(assignmentId < ${assignmentCount}); return assignments[assignmentId]; }
+
+ QScxmlExecutableContent::ForeachInfo foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { Q_ASSERT(foreachId >= 0); Q_ASSERT(foreachId < ${foreachCount}); return foreaches[foreachId]; }
+
+ QString string(QScxmlExecutableContent::StringId id) const Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ Q_ASSERT(id >= QScxmlExecutableContent::NoString); Q_ASSERT(id < ${stringCount});
+ if (id == QScxmlExecutableContent::NoString) return QString();
+ return QString({static_cast<QStringData*>(strings.data + id)});
+ }
+
+ const qint32 *stateMachineTable() const Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return theStateMachineTable; }
+
+ QScxmlInvokableServiceFactory *serviceFactory(int id) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+ int signalIndexForEvent(const QString &event) const Q_DECL_OVERRIDE Q_DECL_FINAL;
+
+ ${classname} &stateMachine;
+ ${dataModelField}
+
+ static qint32 theInstructions[];
+ static QScxmlExecutableContent::StringId dataIds[];
+ static QScxmlExecutableContent::EvaluatorInfo evaluators[];
+ static QScxmlExecutableContent::AssignmentInfo assignments[];
+ static QScxmlExecutableContent::ForeachInfo foreaches[];
+ static const qint32 theStateMachineTable[];
+ static struct Strings {
+ QArrayData data[${stringCount}];
+ qunicodechar stringdata[${stringdataSize}];
+ } strings;
+
+ static std::vector<QString> outgoingEvents;
+};
+
+${classname}::${classname}(QObject *parent)
+ : QScxmlStateMachine(parent)
+ , data(new Data(*this))
+{ qRegisterMetaType<${classname} *>(); data->init(); }
+
+${classname}::~${classname}()
+{ delete data; }
+
+QScxmlInvokableServiceFactory *${classname}::Data::serviceFactory(int id) const
+{
+ switch (id) {
+ ${serviceFactories}
+ }
+}
+
+std::vector<QString> ${classname}::Data::outgoingEvents = {
+${outgoingEvents}
+};
+
+int ${classname}::Data::signalIndexForEvent(const QString &event) const
+{
+ auto it = std::lower_bound(outgoingEvents.begin(), outgoingEvents.end(), event);
+ if (it != outgoingEvents.end() && *it == event) {
+ return int(std::distance(outgoingEvents.begin(), it));
+ } else {
+ return -1;
+ }
+}
+
+qint32 ${classname}::Data::theInstructions[] = {
+${theInstructions}
+};
+
+QScxmlExecutableContent::StringId ${classname}::Data::dataIds[] = {
+${dataIds}
+};
+
+QScxmlExecutableContent::EvaluatorInfo ${classname}::Data::evaluators[] = {
+${evaluators}
+};
+
+QScxmlExecutableContent::AssignmentInfo ${classname}::Data::assignments[] = {
+${assignments}
+};
+
+QScxmlExecutableContent::ForeachInfo ${classname}::Data::foreaches[] = {
+${foreaches}
+};
+
+#define STR_LIT(idx, ofs, len) \
+ Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
+ qptrdiff(offsetof(Strings, stringdata) + ofs * sizeof(qunicodechar) - idx * sizeof(QArrayData)) \
+ )
+${classname}::Data::Strings ${classname}::Data::strings = {{
+${strLits}
+},{
+${uniLits}
+}};
+
+const qint32 ${classname}::Data::theStateMachineTable[] = ${theStateMachineTable};
+
+${metaObject}
+${getters}
+${slots}
diff --git a/tools/qscxmlc/decl.t b/tools/qscxmlc/decl.t
new file mode 100644
index 0000000..2b35d67
--- /dev/null
+++ b/tools/qscxmlc/decl.t
@@ -0,0 +1,21 @@
+class ${classname}: public QScxmlStateMachine
+{
+public:
+ /* qmake ignore Q_OBJECT */
+ Q_OBJECT
+
+public:
+ ${classname}(QObject *parent = 0);
+ ~${classname}();
+
+${getters}
+signals:
+${signals}
+public slots:
+${slots}
+private:
+ struct Data;
+ friend struct Data;
+ struct Data *data;
+};
+
diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp
index b37e928..561bc05 100644
--- a/tools/qscxmlc/generator.cpp
+++ b/tools/qscxmlc/generator.cpp
@@ -1146,13 +1146,13 @@ void Generator::generateStaticMetacall()
Q_ASSERT(!f.normalizedType.isEmpty());
fprintf(out, " case %d: ", methodindex);
- //----
+ //---- Changed from the original in moc
if (f.implementation) {
fprintf(out, f.implementation, methodindex);
fprintf(out, " break;\n");
continue;
}
- //----
+ //---- End of change
if (f.normalizedType != "void")
fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData());
@@ -1327,8 +1327,13 @@ void Generator::generateStaticMetacall()
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());
+ //---- Changed from the original in moc
+ {
+ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n",
+ propindex, p.type.constData(), prefix.constData(), p.read.constData(),
+ p.read.endsWith(')') ? "" : "()");
+ }
+ //---- End of change
else
fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n",
propindex, p.type.constData(), prefix.constData(), p.member.constData());
diff --git a/tools/qscxmlc/qscxmlc.pro b/tools/qscxmlc/qscxmlc.pro
index 7621eef..7620d3d 100644
--- a/tools/qscxmlc/qscxmlc.pro
+++ b/tools/qscxmlc/qscxmlc.pro
@@ -11,3 +11,6 @@ SOURCES += \
main.cpp
load(qt_tool)
+load(resources)
+
+RESOURCES += templates.qrc
diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp
index 6efa5f6..f6cc42a 100644
--- a/tools/qscxmlc/scxmlcppdumper.cpp
+++ b/tools/qscxmlc/scxmlcppdumper.cpp
@@ -33,11 +33,17 @@
#include <functional>
#include <QFileInfo>
#include <QBuffer>
+#include <QFile>
+#include <QResource>
#include "generator.h"
QT_BEGIN_NAMESPACE
+using namespace QScxmlInternal;
+
+namespace {
+
static const QString doNotEditComment = QString::fromLatin1(
"//\n"
"// Statemachine code from reading SCXML file '%1'\n"
@@ -56,91 +62,6 @@ static const QString revisionCheck = QString::fromLatin1(
"#endif\n"
);
-struct StringListDumper {
- StringListDumper &operator <<(const QString &s) {
- text.append(s);
- return *this;
- }
-
- StringListDumper &operator <<(const QLatin1String &s) {
- text.append(s);
- return *this;
- }
- StringListDumper &operator <<(const char *s) {
- text.append(QLatin1String(s));
- return *this;
- }
- StringListDumper &operator <<(int i) {
- text.append(QString::number(i));
- return *this;
- }
- StringListDumper &operator <<(const QByteArray &s) {
- text.append(QString::fromUtf8(s));
- return *this;
- }
-
- bool isEmpty() const {
- return text.isEmpty();
- }
-
- void write(QTextStream &out, const QString &prefix, const QString &suffix, const QString &mainClassName = QString()) const
- {
- foreach (QString line, text) {
- if (!mainClassName.isEmpty() && line.contains(QStringLiteral("%"))) {
- line = line.arg(mainClassName);
- }
- out << prefix << line << suffix;
- }
- }
-
- void unique()
- {
- text.sort();
- text.removeDuplicates();
- }
-
- QStringList text;
-};
-
-struct Method {
- StringListDumper initializer;
- Method(const QString &decl = QString()): decl(decl) {}
- Method(const StringListDumper &impl): impl(impl) {}
- QString decl; // void f(int i = 0);
- StringListDumper impl; // void f(int i) { m_i = ++i; }
-};
-
-struct ClassDump {
- bool needsEventFilter;
- StringListDumper implIncludes;
- QString className;
- QString dataModelClassName;
- StringListDumper classFields;
- StringListDumper tables;
- Method init;
- Method initDataModel;
- StringListDumper dataMethods;
- StringListDumper classMethods;
- Method constructor;
- Method destructor;
- StringListDumper properties;
- StringListDumper signalMethods;
- QList<Method> publicMethods;
- QList<Method> protectedMethods;
- StringListDumper publicSlotDeclarations;
- StringListDumper publicSlotDefinitions;
-
- QList<Method> dataModelMethods;
-
- ClassDump()
- : needsEventFilter(false)
- {}
-
- QByteArray metaData;
-};
-
-namespace {
-
QString cEscape(const QString &str)
{
QString res;
@@ -179,1136 +100,328 @@ QString cEscape(const QString &str)
return str;
}
+typedef QHash<QString, QString> Replacements;
+static void genTemplate(QTextStream &out, const QString &filename, const Replacements &replacements)
+{
+ QResource file(filename);
+ if (!file.isValid()) {
+ qFatal("Unable to open template '%s'", qPrintable(filename));
+ }
+ QByteArray data;
+ if (file.isCompressed() && file.size()) {
+ data = qUncompress(file.data(), int(file.size()));
+ } else {
+ data = QByteArray::fromRawData(reinterpret_cast<const char *>(file.data()),
+ int(file.size()));
+ }
+ const QString t = QString::fromLatin1(data);
+ data.clear();
+
+ int start = 0;
+ for (int openIdx = t.indexOf(QStringLiteral("${"), start); openIdx >= 0; openIdx =
+ t.indexOf(QStringLiteral("${"), start)) {
+ out << t.midRef(start, openIdx - start);
+ openIdx += 2;
+ const int closeIdx = t.indexOf(QLatin1Char('}'), openIdx);
+ Q_ASSERT(closeIdx >= openIdx);
+ QString key = t.mid(openIdx, closeIdx - openIdx);
+ if (!replacements.contains(key)) {
+ qFatal("Replacing '%s' failed: no replacement found", qPrintable(key));
+ }
+ out << replacements.value(key);
+ start = closeIdx + 1;
+ }
+ out << t.midRef(start);
+}
+
static const char *headerStart =
"#include <QScxmlStateMachine>\n"
"#include <QString>\n"
- "#include <QByteArray>\n"
+ "#include <QVariant>\n"
"\n";
using namespace DocumentModel;
-enum class Evaluator
+QString createContainer(const QString &baseType, const QString &elementType,
+ const QStringList &elements, bool useCxx11)
{
- ToVariant,
- ToString,
- ToBool,
- Assignment,
- Foreach,
- Script
-};
-
-class DumperVisitor: public QScxmlExecutableContent::Builder
-{
- Q_DISABLE_COPY(DumperVisitor)
-
-public:
- DumperVisitor(ClassDump &clazz, TranslationUnit *tu)
- : namespacePrefix(QStringLiteral("::"))
- , clazz(clazz)
- , translationUnit(tu)
- , m_bindLate(false)
- , m_qtMode(false)
- {
- if (!tu->namespaceName.isEmpty()) {
- namespacePrefix += QStringLiteral("%1::").arg(tu->namespaceName);
- }
- }
-
- void process(ScxmlDocument *doc)
- {
- Q_ASSERT(doc);
-
- clazz.className = mangleIdentifier(translationUnit->classnameForDocument.value(doc));
- m_qtMode = doc->qtMode;
-
- doc->root->accept(this);
-
- addSubStateMachineProperties(doc);
- addEvents();
-
- generateMetaObject();
- generateTables();
- }
-
- ~DumperVisitor()
- {
- Q_ASSERT(m_parents.isEmpty());
- }
-
-protected:
- using NodeVisitor::visit;
-
- bool visit(Scxml *node) Q_DECL_OVERRIDE
- {
- // init:
- if (!node->name.isEmpty()) {
- clazz.dataMethods << QStringLiteral("QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return string(%1); }").arg(addString(node->name))
- << QString();
- clazz.init.impl << QStringLiteral("stateMachine.setObjectName(string(%1));").arg(addString(node->name));
+ QString result;
+ if (useCxx11) {
+ if (elements.isEmpty()) {
+ result += QStringLiteral("{}");
} else {
- clazz.dataMethods << QStringLiteral("QString name() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return QString(); }")
- << QString();
- }
- if (node->dataModel == Scxml::CppDataModel) {
- // Tell the builder not to generate any script strings when visiting any executable content.
- // We'll take care of the evaluators ourselves.
- setIsCppDataModel(true);
+ result += QStringLiteral("{ ") + elements.join(QStringLiteral(", ")) + QStringLiteral(" }");
}
-
- QString binding;
- switch (node->binding) {
- case Scxml::EarlyBinding:
- binding = QStringLiteral("Early");
- break;
- case Scxml::LateBinding:
- binding = QStringLiteral("Late");
- m_bindLate = true;
- break;
- default:
- Q_UNREACHABLE();
+ } else {
+ result += QStringLiteral("%1<%2>()").arg(baseType, elementType);
+ if (!elements.isEmpty()) {
+ result += QStringLiteral(" << ") + elements.join(QStringLiteral(" << "));
}
- clazz.init.impl << QStringLiteral("stateMachine.setDataBinding(QScxmlStateMachine::%1Binding);").arg(binding);
- clazz.implIncludes << QStringLiteral("qscxmlexecutablecontent.h");
- clazz.init.impl << QStringLiteral("stateMachine.setTableData(this);");
-
- foreach (AbstractState *s, node->initialStates) {
- clazz.init.impl << QStringLiteral("%1.setAsInitialStateFor(&stateMachine);")
- .arg(mangledName(s, StateName));
- }
-
- // visit the kids:
- m_parents.append(node);
- visit(node->children);
- visit(node->dataElements);
-
- m_dataElements.append(node->dataElements);
- if (node->script || !m_dataElements.isEmpty() || !node->initialSetup.isEmpty()) {
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return %1; }").arg(startNewSequence())
- << QString();
- generate(m_dataElements);
- if (node->script) {
- node->script->accept(this);
- }
- visit(&node->initialSetup);
- endSequence();
- } else {
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::ContainerId initialSetup() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return QScxmlExecutableContent::NoInstruction; }")
- << QString();
- }
-
- m_parents.removeLast();
-
- { // the data model:
- switch (node->dataModel) {
- case Scxml::NullDataModel:
- clazz.classFields << QStringLiteral("QScxmlNullDataModel dataModel;");
- clazz.implIncludes << QStringLiteral("QScxmlNullDataModel");
- clazz.init.impl << QStringLiteral("stateMachine.setDataModel(&dataModel);");
- break;
- case Scxml::JSDataModel:
- clazz.classFields << QStringLiteral("QScxmlEcmaScriptDataModel dataModel;");
- clazz.implIncludes << QStringLiteral("QScxmlEcmaScriptDataModel");
- clazz.init.impl << QStringLiteral("stateMachine.setDataModel(&dataModel);");
- break;
- case Scxml::CppDataModel:
- clazz.dataModelClassName = node->cppDataModelClassName;
- clazz.implIncludes << node->cppDataModelHeaderName;
- break;
- default:
- Q_UNREACHABLE();
- }
- }
- return false;
}
+ return result;
+}
- bool visit(State *node) Q_DECL_OVERRIDE
- {
- QString name = mangledName(node, PlainName);
- QString stateName = mangledName(node, StateName);
- // Property stuff:
- if (isValidQPropertyName(node->id)) {
- clazz.properties << QStringLiteral("Q_PROPERTY(bool %1 READ %2 NOTIFY %3)")
- .arg(node->id).arg(name)
- .arg(mangledName(node, SignalName));
- }
- 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) {
- clazz.classFields << QStringLiteral("QScxmlFinalState ") + stateName + QLatin1Char(';');
- } else {
- clazz.classFields << QStringLiteral("QScxmlState ") + stateName + QLatin1Char(';');
- }
-
- // Initializer:
- clazz.constructor.initializer << generateInitializer(node);
-
- // init:
- if (!node->id.isEmpty()) {
- clazz.init.impl << stateName + QStringLiteral(".setObjectName(string(%1));").arg(addString(node->id));
- }
- if (node->type == State::Parallel) {
- clazz.init.impl << stateName + QStringLiteral(".setChildMode(QState::ParallelStates);");
- } else {
- foreach (AbstractState *initialState, node->initialStates) {
- clazz.init.impl << stateName + QStringLiteral(".setInitialState(&")
- + mangledName(initialState, StateName)
- + QStringLiteral(");");
- }
-
- }
- if (!node->id.isEmpty()) {
- clazz.init.impl << QStringLiteral("QObject::connect(&")
- + stateName
- + QStringLiteral(", SIGNAL(activeChanged(bool)), &stateMachine, SIGNAL(")
- + mangledName(node, SignalName)
- + QStringLiteral("(bool)));");
- }
-
- m_stateNames.append(node->id);
- m_stateFieldNames.append(stateName);
-
- // visit the kids:
- m_parents.append(node);
- if (!node->dataElements.isEmpty()) {
- if (m_bindLate) {
- clazz.init.impl << stateName + QStringLiteral(".setInitInstructions(%1);").arg(startNewSequence());
- generate(node->dataElements);
- endSequence();
- } else {
- m_dataElements.append(node->dataElements);
- }
- }
-
- visit(node->children);
- if (!node->onEntry.isEmpty())
- clazz.init.impl << stateName + QStringLiteral(".setOnEntryInstructions(%1);").arg(generate(node->onEntry));
- if (!node->onExit.isEmpty())
- clazz.init.impl << stateName + QStringLiteral(".setOnExitInstructions(%1);").arg(generate(node->onExit));
- if (!node->invokes.isEmpty()) {
- QStringList lines;
- for (int i = 0, ei = node->invokes.size(); i != ei; ++i) {
- Invoke *invoke = node->invokes.at(i);
- QString line = QStringLiteral("new QScxmlInvokeScxmlFactory<%1>(").arg(scxmlClassName(invoke->content.data()));
- line += QStringLiteral("%1, ").arg(Builder::createContext(QStringLiteral("invoke")));
- line += QStringLiteral("%1, ").arg(createEvaluatorString(QStringLiteral("invoke"),
- QStringLiteral("srcexpr"),
- invoke->srcexpr));
- line += QStringLiteral("%1, ").arg(addString(invoke->id));
- line += QStringLiteral("%1, ").arg(addString(node->id + QStringLiteral(".session-")));
- line += QStringLiteral("%1, ").arg(addString(invoke->idLocation));
- {
- QStringList l;
- foreach (const QString &name, invoke->namelist) {
- l.append(QString::number(addString(name)));
- }
- line += QStringLiteral("%1, ").arg(createVector(QStringLiteral("QScxmlExecutableContent::StringId"), l));
- }
- line += QStringLiteral("%1, ").arg(invoke->autoforward ? QStringLiteral("true") : QStringLiteral("false"));
- {
- QStringList l;
- foreach (DocumentModel::Param *param, invoke->params) {
- l += QStringLiteral("QScxmlInvokableServiceFactory::Param(%1, %2, %3)")
- .arg(addString(param->name))
- .arg(createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"), param->expr))
- .arg(addString(param->location));
- }
- line += QStringLiteral("%1, ").arg(createVector(QStringLiteral("QScxmlInvokableServiceFactory::Param"), l));
- }
- if (invoke->finalize.isEmpty()) {
- line += QStringLiteral("QScxmlExecutableContent::NoInstruction");
- } else {
- line += QString::number(startNewSequence());
- visit(&invoke->finalize);
- endSequence();
- }
- line += QLatin1Char(')');
- lines << line;
- }
- clazz.init.impl << stateName + QStringLiteral(".setInvokableServiceFactories(");
- clazz.init.impl << QStringLiteral(" ") + createVector(QStringLiteral("QScxmlInvokableServiceFactory *"), lines);
- clazz.init.impl << QStringLiteral(");");
- }
-
- if (node->type == State::Final) {
- auto id = generate(node->doneData);
- clazz.init.impl << stateName + QStringLiteral(".setDoneData(%1);").arg(id);
- }
-
- m_parents.removeLast();
- return false;
- }
-
- bool visit(Transition *node) Q_DECL_OVERRIDE
- {
- const QString tName = transitionName(node);
- if (m_qtMode) {
- foreach (const QString &event, node->events) {
- if (!DocumentModel::isEventToBeGenerated(event))
- continue;
-
- // If the event name is not filtered out, is was already validated inside:
- // bool ScxmlVerifier::visit(DocumentModel::Transition *transition)
- // by a call to: validateEventName();
- m_knownEvents.insert(event);
- }
- }
-
- // Declaration:
- clazz.classFields << QStringLiteral("QScxmlTransition ") + tName + QLatin1Char(';');
-
- // Initializer:
- QString initializer = tName + QStringLiteral("(");
- QStringList elements;
- foreach (const QString &event, node->events)
- elements.append(qba(event));
- initializer += createList(QStringLiteral("QString"), elements);
- initializer += QStringLiteral(")");
- clazz.constructor.initializer << initializer;
-
- // init:
- if (node->condition) {
- QString condExpr = *node->condition.data();
- auto cond = createEvaluatorBool(QStringLiteral("transition"), QStringLiteral("cond"), condExpr);
- clazz.init.impl << tName + QStringLiteral(".setConditionalExpression(%1);").arg(cond);
- }
-
- if (m_parents.last()->asHistoryState()) {
- clazz.init.impl << QStringLiteral("%1.setDefaultTransition(&%2);").arg(parentStateMemberName(), tName);
- } else {
- clazz.init.impl << QStringLiteral("%1.addTransitionTo(&%2);").arg(tName, parentStateMemberName());
- }
-
- if (node->type == Transition::Internal) {
- clazz.init.impl << tName + QStringLiteral(".setTransitionType(QAbstractTransition::InternalTransition);");
- }
- QStringList targetNames;
- foreach (DocumentModel::AbstractState *s, node->targetStates)
- targetNames.append(QStringLiteral("&") + mangledName(s, StateName));
- QString targets = tName + QStringLiteral(".setTargetStates(") + createList(QStringLiteral("QAbstractState*"), targetNames);
- clazz.init.impl << targets + QStringLiteral(");");
-
- // visit the kids:
- if (!node->instructionsOnTransition.isEmpty()) {
- m_parents.append(node);
- m_currentTransitionName = tName;
- clazz.init.impl << tName + QStringLiteral(".setInstructionsOnTransition(%1);").arg(startNewSequence());
- visit(&node->instructionsOnTransition);
- endSequence();
- m_parents.removeLast();
- m_currentTransitionName.clear();
- }
- return false;
- }
-
- bool visit(DocumentModel::HistoryState *node) Q_DECL_OVERRIDE
- {
- // Includes:
- clazz.implIncludes << "QScxmlHistoryState";
-
- const QString stateName = mangledName(node, StateName);
- // Declaration:
- clazz.classFields << QStringLiteral("QScxmlHistoryState ") + stateName + QLatin1Char(';');
-
- // Initializer:
- clazz.constructor.initializer << generateInitializer(node);
+QString createVector(const QString &elementType, const QStringList &elements, bool useCxx11)
+{ return createContainer(QStringLiteral("QVector"), elementType, elements, useCxx11); }
- // init:
- if (!node->id.isEmpty()) {
- clazz.init.impl << stateName + QStringLiteral(".setObjectName(string(%1));").arg(addString(node->id));
- }
- QString depth;
- switch (node->type) {
- case DocumentModel::HistoryState::Shallow:
- depth = QStringLiteral("Shallow");
- break;
- case DocumentModel::HistoryState::Deep:
- depth = QStringLiteral("Deep");
+static void generateList(QString &out, std::function<QString(int)> next)
+{
+ const int maxLineLength = 80;
+ QString line;
+ for (int i = 0; ; ++i) {
+ const QString nr = next(i);
+ if (nr.isNull())
break;
- default:
- Q_UNREACHABLE();
- }
- clazz.init.impl << stateName + QStringLiteral(".setHistoryType(QScxmlHistoryState::") + depth + QStringLiteral("History);");
-
- // visit the kid:
- if (Transition *t = node->defaultConfiguration()) {
-
- m_parents.append(node);
- t->accept(this);
- m_parents.removeLast();
- }
- return false;
- }
-
- bool visit(Send *node) Q_DECL_OVERRIDE
- {
- 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);
- }
- }
- return QScxmlExecutableContent::Builder::visit(node);
- }
-
-private:
- enum NameForm {
- PlainName,
- SignalName,
- MachineName,
- StateName
- };
+ if (i != 0)
+ line += QLatin1Char(',');
- QString mangledName(const QString &id, NameForm form) const
- {
- QString name = id;
- switch (form) {
- case PlainName: break;
- case SignalName: name.append(QStringLiteral("Changed")); break;
- case StateName: name.prepend(QStringLiteral("state_")); break;
- case MachineName: name.prepend(QStringLiteral("machine_")); break;
+ if (line.length() + nr.length() + 1 > maxLineLength) {
+ out += line + QLatin1Char('\n');
+ line.clear();
+ } else if (i != 0) {
+ line += QLatin1Char(' ');
}
-
- return name.isEmpty() ? name : mangleIdentifier(name);
- }
-
- QString mangledName(AbstractState *state, NameForm form) const
- {
- Q_ASSERT(state);
-
- QString name = m_mangledNames.value(state)[form];
- if (!name.isEmpty())
- return name;
-
- QString id = state->id;
- if (State *s = state->asState()) {
- if (s->type == State::Initial) {
- id = s->parent->asState()->id + QStringLiteral("_initial");
- }
- }
-
- name = mangledName(id, form);
- m_mangledNames[state][form] = name;
- return name;
- }
-
- QString transitionName(Transition *t) const
- {
- int idx = 0;
- QString parentName;
- auto parent = m_parents.last();
- if (State *parentState = parent->asState()) {
- parentName = mangledName(parentState, PlainName);
- idx = childIndex(t, parentState->children);
- } else if (HistoryState *historyState = parent->asHistoryState()) {
- parentName = mangledName(historyState, PlainName);
- } else if (Scxml *scxml = parent->asScxml()) {
- parentName = QStringLiteral("stateMachine");
- idx = childIndex(t, scxml->children);
- } else {
- Q_UNREACHABLE();
- }
- return QStringLiteral("transition_%1_%2").arg(parentName, QString::number(idx));
+ line += nr;
}
+ if (!line.isEmpty())
+ out += line;
+}
- static int childIndex(StateOrTransition *child, const QVector<StateOrTransition *> &children) {
- int idx = 0;
- foreach (StateOrTransition *sot, children) {
- if (sot == child)
- break;
+void generateTables(const GeneratedTableData &td, const QStringList &outgoingEvents,
+ Replacements &replacements, bool useCxx11)
+{
+ { // instructions
+ auto instr = td.theInstructions;
+ QString out;
+ generateList(out, [&instr](int idx) -> QString {
+ if (instr.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("-1");
+ if (idx < instr.size())
+ return QString::number(instr.at(idx));
else
- ++idx;
- }
- return idx;
- }
-
- QString createList(const QString &elementType, const QStringList &elements) const
- { return createContainer(QStringLiteral("QList"), elementType, elements); }
-
- QString createVector(const QString &elementType, const QStringList &elements) const
- { return createContainer(QStringLiteral("QVector"), elementType, elements); }
-
- QString createContainer(const QString &baseType, const QString &elementType, const QStringList &elements) const
- {
- QString result;
- if (translationUnit->useCxx11) {
- if (elements.isEmpty()) {
- result += QStringLiteral("{}");
- } else {
- result += QStringLiteral("{ ") + elements.join(QStringLiteral(", ")) + QStringLiteral(" }");
- }
- } else {
- result += QStringLiteral("%1<%2>()").arg(baseType, elementType);
- if (!elements.isEmpty()) {
- result += QStringLiteral(" << ") + elements.join(QStringLiteral(" << "));
- }
- }
- return result;
- }
-
- QString generateInitializer(AbstractState *node) const
- {
- QString init = mangledName(node, StateName) + QStringLiteral("(");
- if (State *parentState = node->parent->asState()) {
- init += QStringLiteral("&") + mangledName(parentState, StateName);
- } else {
- init += QStringLiteral("&stateMachine");
- }
- init += QLatin1Char(')');
- return init;
- }
-
- void addSubStateMachineProperties(ScxmlDocument *doc)
- {
- foreach (ScxmlDocument *subDocs, doc->allSubDocuments) {
- QString name = subDocs->root->name;
- if (name.isEmpty())
- continue;
- auto plainName = mangledName(name, PlainName);
- auto qualifiedName = namespacePrefix + plainName;
- if (m_serviceProps.contains(qMakePair(plainName, qualifiedName)))
- continue;
- m_serviceProps.append(qMakePair(name, qualifiedName));
- clazz.classFields << QStringLiteral("%1 *%2;").arg(qualifiedName, plainName);
- clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(plainName);
- if (isValidQPropertyName(name)) {
- clazz.properties << QStringLiteral("Q_PROPERTY(%1%2 *%3 READ %2 NOTIFY %4)")
- .arg(namespacePrefix, plainName, name,
- mangledName(name, SignalName));
- }
- if (m_qtMode) {
- Method getter(QStringLiteral("%1 *%2() const").arg(qualifiedName, plainName));
- getter.impl << QStringLiteral("%1 *%2::%3() const").arg(qualifiedName,
- clazz.className, plainName)
- << QStringLiteral("{ return data->%1; }").arg(plainName);
- clazz.publicMethods << getter;
- clazz.signalMethods << QStringLiteral("void %1(%2 *statemachine);")
- .arg(mangledName(name, SignalName), qualifiedName);
+ return QString();
+ });
+ replacements[QStringLiteral("theInstructions")] = out;
+ }
+
+ { // dataIds
+ auto dataIds = td.theDataNameIds;
+ QString out;
+ generateList(out, [&dataIds](int idx) -> QString {
+ if (dataIds.size() == 0 && idx == 0) // prevent generation of empty array
+ return QStringLiteral("-1");
+ if (idx < dataIds.size())
+ return QString::number(dataIds[idx]);
+ else
+ return QString();
+ });
+ replacements[QStringLiteral("dataNameCount")] = QString::number(dataIds.size());
+ replacements[QStringLiteral("dataIds")] = out;
+ }
+
+ { // evaluators
+ auto evaluators = td.theEvaluators;
+ QString out;
+ generateList(out, [&evaluators](int idx) -> QString {
+ if (evaluators.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("{ -1, -1 }");
+ if (idx >= evaluators.size())
+ return QString();
+
+ const auto eval = evaluators.at(idx);
+ return QStringLiteral("{ %1, %2 }").arg(eval.expr).arg(eval.context);
+ });
+ replacements[QStringLiteral("evaluatorCount")] = QString::number(evaluators.size());
+ replacements[QStringLiteral("evaluators")] = out;
+ }
+
+ { // assignments
+ auto assignments = td.theAssignments;
+ QString out;
+ generateList(out, [&assignments](int idx) -> QString {
+ if (assignments.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("{ -1, -1, -1 }");
+ if (idx >= assignments.size())
+ return QString();
+
+ auto assignment = assignments.at(idx);
+ return QStringLiteral("{ %1, %2, %3 }")
+ .arg(assignment.dest).arg(assignment.expr).arg(assignment.context);
+ });
+ replacements[QStringLiteral("assignmentCount")] = QString::number(assignments.size());
+ replacements[QStringLiteral("assignments")] = out;
+ }
+
+ { // foreaches
+ auto foreaches = td.theForeaches;
+ QString out;
+ generateList(out, [&foreaches](int idx) -> QString {
+ if (foreaches.isEmpty() && idx == 0) // prevent generation of empty array
+ return QStringLiteral("{ -1, -1, -1, -1 }");
+ if (idx >= foreaches.size())
+ return QString();
+
+ auto foreachItem = foreaches.at(idx);
+ return QStringLiteral("{ %1, %2, %3, %4 }").arg(foreachItem.array).arg(foreachItem.item)
+ .arg(foreachItem.index).arg(foreachItem.context);
+ });
+ replacements[QStringLiteral("foreachCount")] = QString::number(foreaches.size());
+ replacements[QStringLiteral("foreaches")] = out;
+ }
+
+ { // strings
+ QString out;
+ auto strings = td.theStrings;
+ if (strings.isEmpty()) // prevent generation of empty array
+ strings.append(QStringLiteral(""));
+ int ucharCount = 0;
+ generateList(out, [&ucharCount, &strings](int idx) -> QString {
+ if (idx >= strings.size())
+ return QString();
+
+ const int length = strings.at(idx).size();
+ const QString str = QStringLiteral("STR_LIT(%1, %2, %3)").arg(
+ QString::number(idx), QString::number(ucharCount), QString::number(length));
+ ucharCount += length + 1;
+ return str;
+ });
+ replacements[QStringLiteral("stringCount")] = QString::number(strings.size());
+ replacements[QStringLiteral("strLits")] = out;
+
+ out.clear();
+ for (int i = 0, ei = strings.size(); i < ei; ++i) {
+ const QString &string = strings.at(i);
+ QString result;
+ if (i != 0)
+ result += QLatin1Char('\n');
+ for (int charPos = 0, eCharPos = string.size(); charPos < eCharPos; ++charPos) {
+ result.append(QStringLiteral("0x%1,")
+ .arg(QString::number(string.at(charPos).unicode(), 16)));
}
-
- clazz.dataMethods << QStringLiteral("%1 *%2() const")
- .arg(qualifiedName, mangledName(name, MachineName))
- << QStringLiteral("{ return %1; }").arg(plainName)
- << QString();
+ result.append(QStringLiteral("0%1 // %2: %3")
+ .arg(QLatin1String(i < ei - 1 ? "," : ""), QString::number(i),
+ cEscape(string)));
+ out += result;
}
+ replacements[QStringLiteral("uniLits")] = out;
+ replacements[QStringLiteral("stringdataSize")] = QString::number(ucharCount + 1);
}
- void addEvents()
{
- QStringList knownEventsList = m_knownEvents.toList();
- std::sort(knownEventsList.begin(), knownEventsList.end());
- if (m_qtMode) {
- foreach (const QString &event, knownEventsList) {
- clazz.publicSlotDeclarations << QStringLiteral("void ") + event + QStringLiteral("(const QVariant &eventData = QVariant());");
- clazz.publicSlotDefinitions << QStringLiteral("void ") + clazz.className
- + QStringLiteral("::")
- + event
- + QStringLiteral("(const QVariant &eventData)\n{ submitEvent(data->") + qba(event)
- + QStringLiteral(", eventData); }");
- }
- }
-
- 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 = 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(signalName), mangleIdentifier(signalName));
- }
- }
- dm << QStringLiteral(" return true;")
- << QStringLiteral("}")
- << QString();
+ QStringList items;
+ foreach (const QString &event, outgoingEvents) {
+ items += QStringLiteral("QString({static_cast<QStringData*>(strings.data + %1)})")
+ .arg(td.theStrings.indexOf(event));
}
+ replacements[QStringLiteral("outgoingEvents")] = createContainer(
+ QStringLiteral("std::vector"), QStringLiteral("QString"), items, useCxx11);
}
+}
- QString createContextString(const QString &instrName) const Q_DECL_OVERRIDE
+void generateCppDataModelEvaluators(const GeneratedTableData::DataModelInfo &info,
+ Replacements &replacements)
+{
{
- if (!m_currentTransitionName.isEmpty()) {
- QString state = parentStateName();
- return QStringLiteral("<%1> instruction in transition of state '%2'").arg(instrName, state);
- } else {
- return QStringLiteral("<%1> instruction in state '%2'").arg(instrName, parentStateName());
+ QString evals;
+ for (auto it = info.stringEvaluators.constBegin(), eit = info.stringEvaluators.constEnd();
+ it != eit; ++it) {
+ evals += QStringLiteral(" case %1:\n").arg(it.key());
+ evals += QStringLiteral(" return [this]()->QString{ return %1; }();\n")
+ .arg(it.value());
}
+ replacements[QStringLiteral("evaluateToStringCases")] = evals;
}
- QString createContext(const QString &instrName, const QString &attrName, const QString &attrValue) const Q_DECL_OVERRIDE
- {
- QString location = createContextString(instrName);
- return QStringLiteral("%1 with %2=\"%3\"").arg(location, attrName, attrValue);
- }
-
- QString parentStateName() const
{
- for (int i = m_parents.size() - 1; i >= 0; --i) {
- Node *node = m_parents.at(i);
- if (State *s = node->asState())
- return s->id;
- else if (HistoryState *h = node->asHistoryState())
- return h->id;
- else if (Scxml *l = node->asScxml())
- return l->name;
+ QString evals;
+ for (auto it = info.boolEvaluators.constBegin(), eit = info.boolEvaluators.constEnd();
+ it != eit; ++it) {
+ evals += QStringLiteral(" case %1:\n").arg(it.key());
+ evals += QStringLiteral(" return [this]()->bool{ return %1; }();\n")
+ .arg(it.value());
}
-
- return QString();
+ replacements[QStringLiteral("evaluateToBoolCases")] = evals;
}
- QString parentStateMemberName() const
{
- Node *parent = m_parents.last();
- if (State *s = parent->asState())
- return mangledName(s, StateName);
- else if (HistoryState *h = parent->asHistoryState())
- return mangledName(h, StateName);
- else if (parent->asScxml())
- return QStringLiteral("stateMachine");
- else
- Q_UNIMPLEMENTED();
- return QString();
- }
-
- static void generateList(StringListDumper &t, std::function<QString(int)> next)
- {
- const int maxLineLength = 80;
- QString line;
- for (int i = 0; ; ++i) {
- QString nr = next(i);
- if (nr.isNull())
- break;
-
- if (i != 0)
- line += QLatin1Char(',');
-
- if (line.length() + nr.length() + 1 > maxLineLength) {
- t << line;
- line.clear();
- } else if (i != 0) {
- line += QLatin1Char(' ');
- }
- line += nr;
+ QString evals;
+ for (auto it = info.variantEvaluators.constBegin(), eit = info.variantEvaluators.constEnd();
+ it != eit; ++it) {
+ evals += QStringLiteral(" case %1:\n").arg(it.key());
+ evals += QStringLiteral(" return [this]()->QVariant{ return %1; }();\n")
+ .arg(it.value());
}
- if (!line.isEmpty())
- t << line;
+ replacements[QStringLiteral("evaluateToVariantCases")] = evals;
}
- void generateTables()
{
- StringListDumper &t = clazz.tables;
- clazz.classFields << QString();
- QScopedPointer<QScxmlExecutableContent::DynamicTableData> td(tableData());
-
- { // instructions
- clazz.classFields << QStringLiteral("static qint32 theInstructions[];");
- t << QStringLiteral("qint32 %1::Data::theInstructions[] = {").arg(clazz.className);
- auto instr = td->instructionTable();
- generateList(t, [&instr](int idx) -> QString {
- if (instr.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("-1");
- if (idx < instr.size())
- return QString::number(instr.at(idx));
- else
- return QString();
- });
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::Instructions instructions() const Q_DECL_OVERRIDE Q_DECL_FINAL")
- << QStringLiteral("{ return theInstructions; }")
- << QString();
- }
-
- { // dataIds
- int count;
- auto dataIds = td->dataNames(&count);
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::StringId dataIds[];");
- t << QStringLiteral("QScxmlExecutableContent::StringId %1::Data::dataIds[] = {").arg(clazz.className);
- if (isCppDataModel()) {
- t << QStringLiteral("-1");
- } else {
- generateList(t, [&dataIds, count](int idx) -> QString {
- if (count == 0 && idx == 0) // prevent generation of empty array
- return QStringLiteral("-1");
- if (idx < count)
- return QString::number(dataIds[idx]);
- else
- return QString();
- });
- }
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::StringId *dataNames(int *count) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ *count = %1; return dataIds; }").arg(count);
- clazz.dataMethods << QString();
- }
-
- { // evaluators
- auto evaluators = td->evaluators();
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::EvaluatorInfo evaluators[];");
- t << QStringLiteral("QScxmlExecutableContent::EvaluatorInfo %1::Data::evaluators[] = {").arg(clazz.className);
- if (isCppDataModel()) {
- t << QStringLiteral("{ -1, -1 }");
- } else {
- generateList(t, [&evaluators](int idx) -> QString {
- if (evaluators.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("{ -1, -1 }");
- if (idx >= evaluators.size())
- return QString();
-
- auto eval = evaluators.at(idx);
- return QStringLiteral("{ %1, %2 }").arg(eval.expr).arg(eval.context);
- });
- }
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::EvaluatorInfo evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ Q_ASSERT(evaluatorId >= 0); Q_ASSERT(evaluatorId < %1); return evaluators[evaluatorId]; }").arg(evaluators.size());
- clazz.dataMethods << QString();
-
- if (isCppDataModel()) {
- {
- StringListDumper stringEvals;
- stringEvals << QStringLiteral("QString %1::evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = stringEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- stringEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" return [this]()->QString{ return %1; }();").arg(it.value());
- }
- stringEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" return QString();")
- << QStringLiteral(" }");
- stringEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(stringEvals));
- }
-
- {
- StringListDumper boolEvals;
- boolEvals << QStringLiteral("bool %1::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = boolEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- boolEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" return [this]()->bool{ return %1; }();").arg(it.value());
- }
- boolEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" return false;")
- << QStringLiteral(" }");
- boolEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(boolEvals));
- }
-
- {
- StringListDumper variantEvals;
- variantEvals << QStringLiteral("QVariant %1::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = variantEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- variantEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" return [this]()->QVariant{ return %1; }();").arg(it.value());
- }
- variantEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" return QVariant();")
- << QStringLiteral(" }");
- variantEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(variantEvals));
- }
-
- {
- StringListDumper voidEvals;
- voidEvals << QStringLiteral("void %1::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) {").arg(clazz.dataModelClassName)
- << QStringLiteral(" *ok = true;")
- << QStringLiteral(" switch (id) {");
- auto evals = voidEvaluators();
- for (auto it = evals.constBegin(), eit = evals.constEnd(); it != eit; ++it) {
- voidEvals << QStringLiteral(" case %1:").arg(it.key())
- << QStringLiteral(" [this]()->void{ %1 }();").arg(it.value())
- << QStringLiteral(" break;");
- }
- voidEvals << QStringLiteral(" default:")
- << QStringLiteral(" Q_UNREACHABLE();")
- << QStringLiteral(" *ok = false;")
- << QStringLiteral(" }");
- voidEvals << QStringLiteral("}");
- clazz.dataModelMethods.append(Method(voidEvals));
- }
- }
- }
-
- { // assignments
- auto assignments = td->assignments();
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::AssignmentInfo assignments[];");
- t << QStringLiteral("QScxmlExecutableContent::AssignmentInfo %1::Data::assignments[] = {").arg(clazz.className);
- if (isCppDataModel()) {
- t << QStringLiteral("{ -1, -1, -1 }");
- } else {
- generateList(t, [&assignments](int idx) -> QString {
- if (assignments.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("{ -1, -1, -1 }");
- if (idx >= assignments.size())
- return QString();
-
- auto ass = assignments.at(idx);
- return QStringLiteral("{ %1, %2, %3 }").arg(ass.dest).arg(ass.expr).arg(ass.context);
- });
- }
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::AssignmentInfo assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ Q_ASSERT(assignmentId >= 0); Q_ASSERT(assignmentId < %1); return assignments[assignmentId]; }").arg(assignments.size());
- clazz.dataMethods << QString();
- }
-
- { // foreaches
- auto foreaches = td->foreaches();
- clazz.classFields << QStringLiteral("static QScxmlExecutableContent::ForeachInfo foreaches[];");
- t << QStringLiteral("QScxmlExecutableContent::ForeachInfo %1::Data::foreaches[] = {").arg(clazz.className);
- generateList(t, [&foreaches](int idx) -> QString {
- if (foreaches.isEmpty() && idx == 0) // prevent generation of empty array
- return QStringLiteral("{ -1, -1, -1, -1 }");
- if (idx >= foreaches.size())
- return QString();
-
- auto foreach = foreaches.at(idx);
- return QStringLiteral("{ %1, %2, %3, %4 }").arg(foreach.array).arg(foreach.item).arg(foreach.index).arg(foreach.context);
- });
- t << QStringLiteral("};") << QStringLiteral("");
- clazz.dataMethods << QStringLiteral("QScxmlExecutableContent::ForeachInfo foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{ Q_ASSERT(foreachId >= 0); Q_ASSERT(foreachId < %1); return foreaches[foreachId]; }").arg(foreaches.size());
- }
-
- { // strings
- t << QStringLiteral("#define STR_LIT(idx, ofs, len) \\")
- << QStringLiteral(" Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \\")
- << QStringLiteral(" qptrdiff(offsetof(Strings, stringdata) + ofs * sizeof(qunicodechar) - idx * sizeof(QArrayData)) \\")
- << QStringLiteral(" )");
-
- t << QStringLiteral("%1::Data::Strings %1::Data::strings = {{").arg(clazz.className);
- auto strings = td->stringTable();
- if (strings.isEmpty()) // prevent generation of empty array
- strings.append(QStringLiteral(""));
- int ucharCount = 0;
- generateList(t, [&ucharCount, &strings](int idx) -> QString {
- if (idx >= strings.size())
- return QString();
-
- int length = strings.at(idx).size();
- QString str = QStringLiteral("STR_LIT(%1, %2, %3)").arg(QString::number(idx),
- QString::number(ucharCount),
- QString::number(length));
- ucharCount += length + 1;
- return str;
- });
- t << QStringLiteral("},{");
- for (int i = 0, ei = strings.size(); i < ei; ++i) {
- const QString &string = strings.at(i);
- QString result;
- for (int charPos = 0, eCharPos = string.size(); charPos < eCharPos; ++charPos) {
- result.append(QStringLiteral("0x%1,")
- .arg(QString::number(string.at(charPos).unicode(), 16)));
- }
- result.append(QStringLiteral("0%1 // %2: %3")
- .arg(QLatin1String(i < ei - 1 ? "," : ""), QString::number(i),
- cEscape(string)));
- t << result;
- }
- t << QStringLiteral("}};") << QStringLiteral("");
-
- clazz.classFields << QStringLiteral("static struct Strings {")
- << QStringLiteral(" QArrayData data[%1];").arg(strings.size())
- << QStringLiteral(" qunicodechar stringdata[%1];").arg(ucharCount + 1)
- << QStringLiteral("} strings;");
-
- clazz.dataMethods << QStringLiteral("QString string(QScxmlExecutableContent::StringId id) const Q_DECL_OVERRIDE Q_DECL_FINAL");
- clazz.dataMethods << QStringLiteral("{");
- clazz.dataMethods << QStringLiteral(" Q_ASSERT(id >= QScxmlExecutableContent::NoString); Q_ASSERT(id < %1);").arg(strings.size());
- clazz.dataMethods << QStringLiteral(" if (id == QScxmlExecutableContent::NoString) return QString();");
- if (translationUnit->useCxx11) {
- clazz.dataMethods << QStringLiteral(" return QString({static_cast<QStringData*>(strings.data + id)});");
- } else {
- clazz.dataMethods << QStringLiteral(" QStringDataPtr data;");
- clazz.dataMethods << QStringLiteral(" data.ptr = static_cast<QStringData*>(strings.data + id);");
- clazz.dataMethods << QStringLiteral(" return QString(data);");
- }
- clazz.dataMethods << QStringLiteral("}");
- clazz.dataMethods << QString();
+ QString evals;
+ for (auto it = info.voidEvaluators.constBegin(), eit = info.voidEvaluators.constEnd();
+ it != eit; ++it) {
+ evals += QStringLiteral(" case %1:\n").arg(it.key());
+ evals += QStringLiteral(" [this]()->void{ %1 }();\n").arg(it.value());
+ evals += QStringLiteral(" break;\n");
}
+ replacements[QStringLiteral("evaluateToVoidCases")] = evals;
}
+}
- void generateMetaObject()
+int createFactoryId(QStringList &factories, const QString &className,
+ const QString &namespacePrefix,
+ QScxmlExecutableContent::StringId invokeLocation,
+ QScxmlExecutableContent::EvaluatorId srcexpr,
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId idPrefix,
+ QScxmlExecutableContent::StringId idlocation,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ bool autoforward,
+ const QVector<QScxmlExecutableContent::Param> &params,
+ QScxmlExecutableContent::ContainerId finalize,
+ bool useCxx11)
+{
+ const int idx = factories.size();
+
+ QString line = QStringLiteral("case %1: return new ").arg(QString::number(idx));
+ if (srcexpr == QScxmlExecutableContent::NoInstruction) {
+ line += QStringLiteral("QScxmlInvokeScxmlFactory<%1::%2>(%3, ")
+ .arg(namespacePrefix, className, QString::number(invokeLocation));
+ } else {
+ line += QStringLiteral("QScxmlDynamicScxmlFactory(%1, %2, ")
+ .arg(invokeLocation).arg(srcexpr);
+ }
+ line += QStringLiteral("%1, ").arg(QString::number(id));
+ line += QStringLiteral("%1, ").arg(QString::number(idPrefix));
+ line += QStringLiteral("%1, ").arg(QString::number(idlocation));
{
- 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;
-
- const QByteArray mangledStateName = mangledName(stateName, StateName).toUtf8();
- const QString mangledSignalName = mangledName(stateName, SignalName);
-
- FunctionDef signal;
- signal.type.name = "void";
- signal.type.rawName = signal.type.name;
- signal.normalizedType = signal.type.name;
- signal.name = mangledSignalName.toUtf8();
- signal.access = FunctionDef::Private;
- signal.isSignal = true;
- if (!m_qtMode) {
- signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);";
- } else {
- clazz.signalMethods << QStringLiteral("void %1(bool active);")
- .arg(mangledSignalName);
- }
- 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->" + mangledStateName + ".active";
- prop.notify = mangledSignalName.toUtf8();
- 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);");
+ QStringList l;
+ foreach (auto name, namelist) {
+ l.append(QString::number(name));
}
- for (const auto &service : m_serviceProps) {
- auto serviceName = service.first;
- const QString mangledServiceName = mangledName(serviceName, PlainName);
- const QString fqServiceClass = service.second;
- const QByteArray serviceClass = fqServiceClass.toUtf8();
- knownQObjectClasses.insert(serviceClass, "");
-
- reg.impl << QStringLiteral(" SET_SERVICE_PROP(%1, %2, %3%2, %4)")
- .arg(addString(serviceName))
- .arg(mangledServiceName, namespacePrefix).arg(classDef.signalList.size());
-
- const QByteArray mangledMachineName = mangledName(serviceName, MachineName).toUtf8();
- const QByteArray mangledSignalName = mangledName(serviceName, SignalName).toUtf8();
-
- FunctionDef signal;
- signal.type.name = "void";
- signal.type.rawName = signal.type.name;
- signal.normalizedType = signal.type.name;
- signal.name = mangledSignalName;
- 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->" + mangledMachineName;
- prop.notify = mangledSignalName;
- 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);
- Generator(&classDef, QList<QByteArray>(), knownQObjectClasses,
- QHash<QByteArray, QByteArray>(), buf).generateCode();
- buf.close();
- }
-
- QString qba(const QString &bytes)
- {
- return QStringLiteral("string(%1)").arg(addString(bytes));
+ line += QStringLiteral("%1, ").arg(
+ createVector(QStringLiteral("QScxmlExecutableContent::StringId"), l, useCxx11));
}
-
- QString scxmlClassName(DocumentModel::ScxmlDocument *doc)
+ line += QStringLiteral("%1, ").arg(autoforward ? QStringLiteral("true")
+ : QStringLiteral("false"));
{
- QString name = mangleIdentifier(translationUnit->classnameForDocument.value(doc));
- Q_ASSERT(!name.isEmpty());
- return namespacePrefix + name;
- }
-
- /*!
- * \internal
- * Mangles \a str to be a unique C++ identifier. Characters that are invalid for C++ identifiers
- * are replaced by the pattern \c _0x<hex>_ where <hex> is the hexadecimal unicode
- * representation of the character. As identifiers with leading underscores followed by either
- * another underscore or a capital letter are reserved in C++, we also escape those, by escaping
- * the first underscore, using the above method.
- *
- * We keep track of all identifiers we have used so far and if we find two different names that
- * map to the same mangled identifier by the above method, we append underscores to the new one
- * until the result is unique.
- *
- * \note
- * Although C++11 allows for non-ascii (unicode) characters to be used in identifiers,
- * many compilers forgot to read the spec and do not implement this. Some also do not
- * implement C99 identifiers, because that is \e {at the implementation's discretion}. So,
- * we are stuck with plain old boring identifiers.
- */
- QString mangleIdentifier(const QString &str) const
- {
- auto isNonDigit = [](QChar c) -> bool {
- return (c >= QLatin1Char('a') && c <= QLatin1Char('z')) ||
- (c >= QLatin1Char('A') && c <= QLatin1Char('Z')) ||
- c == QLatin1Char('_');
- };
-
- Q_ASSERT(!str.isEmpty());
-
- QString mangled;
- mangled.reserve(str.size());
-
- int i = 0;
- if (str.startsWith(QLatin1Char('_')) && str.size() > 1) {
- QChar ch = str.at(1);
- if (ch == QLatin1Char('_')
- || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) {
- mangled += QLatin1String("_0x5f_");
- ++i;
- }
+ QStringList l;
+ foreach (const auto &param, params) {
+ l += QStringLiteral("QScxmlExecutableContent::Param(%1, %2, %3)")
+ .arg(QString::number(param.name),
+ QString::number(param.expr),
+ QString::number(param.location));
}
-
- for (int ei = str.length(); i != ei; ++i) {
- auto c = str.at(i).unicode();
- if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || isNonDigit(c)) {
- mangled += c;
- } else {
- mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_');
- }
- }
-
- while (true) {
- auto it = m_mangledToOriginal.constFind(mangled);
- if (it == m_mangledToOriginal.constEnd()) {
- m_mangledToOriginal.insert(mangled, str);
- break;
- } else if (it.value() == str) {
- break;
- }
- mangled += QStringLiteral("_"); // append underscores until we get a unique name
- }
-
- return mangled;
+ line += QStringLiteral("%1, ").arg(
+ createVector(QStringLiteral("QScxmlExecutableContent::Param"), l,
+ useCxx11));
}
+ line += QStringLiteral("%1);").arg(finalize);
-private:
- QString namespacePrefix;
- ClassDump &clazz;
- TranslationUnit *translationUnit;
- mutable QHash<AbstractState *, QHash<NameForm, QString> > m_mangledNames;
- mutable QHash<QString, QString> m_mangledToOriginal;
- 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;
- QVector<DocumentModel::DataElement *> m_dataElements;
-};
+ factories.append(line);
+ return idx;
+}
} // anonymous namespace
-
void CppDumper::dump(TranslationUnit *unit)
{
Q_ASSERT(unit);
@@ -1316,38 +429,83 @@ void CppDumper::dump(TranslationUnit *unit)
m_translationUnit = unit;
- QStringList classDecls;
- QVector<ClassDump> clazzes;
+ QString namespacePrefix;
+ if (!m_translationUnit->namespaceName.isEmpty()) {
+ namespacePrefix = QStringLiteral("::%1").arg(m_translationUnit->namespaceName);
+ }
+
+ QStringList classNames;
+ QVector<GeneratedTableData> tables;
+ QVector<GeneratedTableData::MetaDataInfo> metaDataInfos;
+ QVector<GeneratedTableData::DataModelInfo> dataModelInfos;
+ QVector<QStringList> factories;
auto docs = m_translationUnit->otherDocuments();
- clazzes.resize(docs.size() + 1);
- DumperVisitor(clazzes[0], m_translationUnit).process(unit->mainDocument);
+ docs.prepend(unit->mainDocument);
+ tables.resize(docs.size());
+ metaDataInfos.resize(tables.size());
+ dataModelInfos.resize(tables.size());
+ factories.resize(tables.size());
+ auto classnameForDocument = unit->classnameForDocument;
+
for (int i = 0, ei = docs.size(); i != ei; ++i) {
auto doc = docs.at(i);
- ClassDump &clazz = clazzes[i + 1];
- DumperVisitor(clazz, m_translationUnit).process(doc);
- classDecls.append(clazz.className);
+ auto metaDataInfo = &metaDataInfos[i];
+ GeneratedTableData::build(doc, &tables[i], metaDataInfo, &dataModelInfos[i],
+ [this, &factories, i, &classnameForDocument, &namespacePrefix](
+ QScxmlExecutableContent::StringId invokeLocation,
+ QScxmlExecutableContent::EvaluatorId srcexpr,
+ QScxmlExecutableContent::StringId id,
+ QScxmlExecutableContent::StringId idPrefix,
+ QScxmlExecutableContent::StringId idlocation,
+ const QVector<QScxmlExecutableContent::StringId> &namelist,
+ bool autoforward,
+ const QVector<QScxmlExecutableContent::Param> &params,
+ QScxmlExecutableContent::ContainerId finalize,
+ const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int {
+ QString className;
+ if (srcexpr == QScxmlExecutableContent::NoInstruction) {
+ className = mangleIdentifier(classnameForDocument.value(content.data()));
+ }
+ return createFactoryId(factories[i], className, namespacePrefix, invokeLocation,
+ srcexpr, id, idPrefix, idlocation, namelist, autoforward, params,
+ finalize, m_translationUnit->useCxx11);
+ });
+ classNames.append(mangleIdentifier(classnameForDocument.value(doc)));
+ std::sort(metaDataInfo->outgoingEvents.begin(), metaDataInfo->outgoingEvents.end());
}
- QString headerName = QFileInfo(unit->outHFileName).fileName();
+ const QString headerName = QFileInfo(unit->outHFileName).fileName();
const QString headerGuard = headerName.toUpper()
.replace(QLatin1Char('.'), QLatin1Char('_'))
.replace(QLatin1Char('-'), QLatin1Char('_'));
- writeHeaderStart(headerGuard, classDecls);
- writeImplStart(clazzes);
+ QStringList forwardDecls = classNames;
+ forwardDecls.pop_front();
+ writeHeaderStart(headerGuard, forwardDecls);
+ writeImplStart();
+
+ for (int i = 0, ei = tables.size(); i != ei; ++i) {
+ const GeneratedTableData &table = tables.at(i);
+ DocumentModel::ScxmlDocument *doc = docs.at(i);
+ writeClass(classNames.at(i), metaDataInfos.at(i));
+ writeImplBody(table, classNames.at(i), doc, factories.at(i), metaDataInfos.at(i));
- foreach (const ClassDump &clazz, clazzes) {
- writeClass(clazz);
- writeImplBody(clazz);
+ if (doc->root->dataModel == DocumentModel::Scxml::CppDataModel) {
+ Replacements r;
+ r[QStringLiteral("datamodel")] = doc->root->cppDataModelClassName;
+ generateCppDataModelEvaluators(dataModelInfos.at(i), r);
+ genTemplate(cpp, QStringLiteral(":/cppdatamodel.t"), r);
+ }
}
- classDecls.append(clazzes.at(0).className);
- writeHeaderEnd(headerGuard, classDecls);
+ writeHeaderEnd(headerGuard, classNames);
writeImplEnd();
}
void CppDumper::writeHeaderStart(const QString &headerGuard, const QStringList &forwardDecls)
{
- h << doNotEditComment.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR))
+ h << doNotEditComment.arg(m_translationUnit->scxmlFileName,
+ QString::number(Q_QSCXMLC_OUTPUT_REVISION),
+ QString::fromLatin1(QT_VERSION_STR))
<< endl;
h << QStringLiteral("#ifndef ") << headerGuard << endl
@@ -1358,59 +516,22 @@ void CppDumper::writeHeaderStart(const QString &headerGuard, const QStringList &
h << l("namespace ") << m_translationUnit->namespaceName << l(" {") << endl << endl;
if (!forwardDecls.isEmpty()) {
- foreach (const QString &name, forwardDecls) {
- h << QStringLiteral("class %1;").arg(name) << endl;
+ for (int i = 1, ei = forwardDecls.size(); i != ei; ++i) {
+ h << QStringLiteral("class %1;").arg(forwardDecls.at(i)) << endl;
}
h << endl;
}
}
-void CppDumper::writeClass(const ClassDump &clazz)
+void CppDumper::writeClass(const QString &className, const GeneratedTableData::MetaDataInfo &info)
{
- h << l("class ") << clazz.className << QStringLiteral(": public QScxmlStateMachine\n{") << endl;
- h << QStringLiteral("public:") << endl
- << QStringLiteral(" /* qmake ignore Q_OBJECT */") << endl
- << QStringLiteral(" Q_OBJECT") << endl;
- clazz.properties.write(h, QStringLiteral(" "), QStringLiteral("\n"));
-
- h << endl
- << QStringLiteral("public:") << endl;
- h << l(" ") << clazz.className << l("(QObject *parent = 0);") << endl;
- h << l(" ~") << clazz.className << "();" << endl;
-
- if (!clazz.publicMethods.isEmpty()) {
- h << endl;
- foreach (const Method &m, clazz.publicMethods) {
- h << QStringLiteral(" ") << m.decl << QLatin1Char(';') << endl;
- }
- }
-
- if (!clazz.protectedMethods.isEmpty()) {
- h << endl
- << QStringLiteral("protected:") << endl;
- foreach (const Method &m, clazz.protectedMethods) {
- h << QStringLiteral(" ") << m.decl << QLatin1Char(';') << endl;
- }
- }
-
- if (!clazz.signalMethods.isEmpty()) {
- h << endl
- << QStringLiteral("signals:") << endl;
- clazz.signalMethods.write(h, QStringLiteral(" "), QStringLiteral("\n"));
- }
-
- if (!clazz.publicSlotDeclarations.isEmpty()) {
- h << endl
- << QStringLiteral("public slots:") << endl;
- clazz.publicSlotDeclarations.write(h, QStringLiteral(" "), QStringLiteral("\n"));
- }
-
- h << endl
- << l("private:") << endl
- << l(" struct Data;") << endl
- << l(" friend struct Data;") << endl
- << l(" struct Data *data;") << endl
- << l("};") << endl << endl;
+ const bool qtMode = m_translationUnit->mainDocument->qtMode;
+ Replacements r;
+ r[QStringLiteral("classname")] = className;
+ r[QStringLiteral("signals")] = qtMode ? generateSignalDecls(info) : QString();
+ r[QStringLiteral("slots")] = qtMode ? generateSlotDecls(info) : QString();
+ r[QStringLiteral("getters")] = qtMode ? generateGetterDecls(info) : QString();
+ genTemplate(h, QStringLiteral(":/decl.t"), r);
}
void CppDumper::writeHeaderEnd(const QString &headerGuard, const QStringList &metatypeDecls)
@@ -1430,121 +551,423 @@ void CppDumper::writeHeaderEnd(const QString &headerGuard, const QStringList &me
h << QStringLiteral("#endif // ") << headerGuard << endl;
}
-void CppDumper::writeImplStart(const QVector<ClassDump> &allClazzes)
+void CppDumper::writeImplStart()
{
- cpp << doNotEditComment.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR))
+ cpp << doNotEditComment.arg(m_translationUnit->scxmlFileName,
+ QString::number(Q_QSCXMLC_OUTPUT_REVISION),
+ l(QT_VERSION_STR))
<< endl;
- StringListDumper includes;
- foreach (const ClassDump &clazz, allClazzes) {
- includes.text += clazz.implIncludes.text;
+ QStringList includes;
+ foreach (DocumentModel::ScxmlDocument *doc, m_translationUnit->classnameForDocument.keys()) {
+ switch (doc->root->dataModel) {
+ case DocumentModel::Scxml::NullDataModel:
+ includes += l("QScxmlNullDataModel");
+ break;
+ case DocumentModel::Scxml::JSDataModel:
+ includes += l("QScxmlEcmaScriptDataModel");
+ break;
+ case DocumentModel::Scxml::CppDataModel:
+ includes += doc->root->cppDataModelHeaderName;
+ break;
+ }
+
}
- includes.unique();
+ includes.sort();
+ includes.removeDuplicates();
QString headerName = QFileInfo(m_translationUnit->outHFileName).fileName();
cpp << l("#include \"") << headerName << l("\"") << endl;
cpp << endl
- << QStringLiteral("#include <qscxmlqstates.h>") << endl
+ << QStringLiteral("#include <qscxmlinvokableservice.h>") << endl
<< QStringLiteral("#include <qscxmltabledata.h>") << endl;
- if (!includes.isEmpty()) {
- includes.write(cpp, QStringLiteral("#include <"), QStringLiteral(">\n"));
- cpp << endl;
+ foreach (const QString &inc, includes) {
+ cpp << l("#include <") << inc << l(">") << endl;
}
cpp << endl
- << revisionCheck.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR))
+ << revisionCheck.arg(m_translationUnit->scxmlFileName,
+ QString::number(Q_QSCXMLC_OUTPUT_REVISION),
+ QString::fromLatin1(QT_VERSION_STR))
<< endl;
if (!m_translationUnit->namespaceName.isEmpty())
cpp << l("namespace ") << m_translationUnit->namespaceName << l(" {") << endl << endl;
}
-void CppDumper::writeImplBody(const ClassDump &clazz)
+void CppDumper::writeImplBody(const GeneratedTableData &table,
+ const QString &className,
+ DocumentModel::ScxmlDocument *doc,
+ const QStringList &factory,
+ const GeneratedTableData::MetaDataInfo &info)
+{
+ const bool qtMode = m_translationUnit->mainDocument->qtMode;
+
+ QString dataModelField, dataModelInitialization;
+ switch (doc->root->dataModel) {
+ case DocumentModel::Scxml::NullDataModel:
+ dataModelField = l("QScxmlNullDataModel dataModel;");
+ dataModelInitialization = l("stateMachine.setDataModel(&dataModel);");
+ break;
+ case DocumentModel::Scxml::JSDataModel:
+ dataModelField = l("QScxmlEcmaScriptDataModel dataModel;");
+ dataModelInitialization = l("stateMachine.setDataModel(&dataModel);");
+ break;
+ case DocumentModel::Scxml::CppDataModel:
+ dataModelField = QStringLiteral("// Data model %1 is set from outside.").arg(
+ doc->root->cppDataModelClassName);
+ dataModelInitialization = dataModelField;
+ break;
+ }
+
+ QString name;
+ if (table.theName == -1) {
+ name = QStringLiteral("QString()");
+ } else {
+ name = QStringLiteral("string(%1)").arg(table.theName);
+ }
+
+ QString serviceFactories = factory.join(QStringLiteral("\n "))
+ + QStringLiteral("\n default: Q_UNREACHABLE();");
+
+ Replacements r;
+ r[QStringLiteral("classname")] = className;
+ r[QStringLiteral("name")] = name;
+ r[QStringLiteral("initialSetup")] = QString::number(table.initialSetup());
+ generateTables(table, info.outgoingEvents, r, m_translationUnit->useCxx11);
+ r[QStringLiteral("dataModelField")] = dataModelField;
+ r[QStringLiteral("dataModelInitialization")] = dataModelInitialization;
+ r[QStringLiteral("theStateMachineTable")] =
+ GeneratedTableData::toString(table.stateMachineTable());
+ r[QStringLiteral("metaObject")] = generateMetaObject(className, info, qtMode);
+ r[QStringLiteral("serviceFactories")] = serviceFactories;
+ r[QStringLiteral("slots")] = qtMode ? generateSlotDefs(className, info) : QString();
+ r[QStringLiteral("getters")] = qtMode ? generateGetterDefs(className, info) : QString();
+ genTemplate(cpp, QStringLiteral(":/data.t"), r);
+}
+
+void CppDumper::writeImplEnd()
{
- cpp << l("struct ") << clazz.className << l("::Data: private QScxmlTableData");
- if (clazz.needsEventFilter) {
- cpp << QStringLiteral(", public QScxmlEventFilter");
+ if (!m_translationUnit->namespaceName.isEmpty()) {
+ cpp << endl
+ << QStringLiteral("} // %1 namespace").arg(m_translationUnit->namespaceName) << endl;
}
- cpp << l(" {") << endl;
-
- cpp << QStringLiteral(" Data(%1 &stateMachine)").arg(clazz.className) << endl
- << QStringLiteral(" : stateMachine(stateMachine)") << endl;
- clazz.constructor.initializer.write(cpp, QStringLiteral(" , "), QStringLiteral("\n"));
- cpp << l(" {") << endl;
- clazz.constructor.impl.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
- cpp << l(" }") << endl;
-
- cpp << endl;
- cpp << l(" void init() {\n");
- clazz.init.impl.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
- cpp << l(" }") << endl;
- cpp << endl;
- clazz.dataMethods.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
+}
- cpp << endl
- << QStringLiteral(" %1 &stateMachine;").arg(clazz.className) << endl;
- clazz.classFields.write(cpp, QStringLiteral(" "), QStringLiteral("\n"));
+/*!
+ * \internal
+ * Mangles \a str to be a unique C++ identifier. Characters that are invalid for C++ identifiers
+ * are replaced by the pattern \c _0x<hex>_ where <hex> is the hexadecimal unicode
+ * representation of the character. As identifiers with leading underscores followed by either
+ * another underscore or a capital letter are reserved in C++, we also escape those, by escaping
+ * the first underscore, using the above method.
+ *
+ * We keep track of all identifiers we have used so far and if we find two different names that
+ * map to the same mangled identifier by the above method, we append underscores to the new one
+ * until the result is unique.
+ *
+ * \note
+ * Although C++11 allows for non-ascii (unicode) characters to be used in identifiers,
+ * many compilers forgot to read the spec and do not implement this. Some also do not
+ * implement C99 identifiers, because that is \e {at the implementation's discretion}. So,
+ * we are stuck with plain old boring identifiers.
+ */
+QString CppDumper::mangleIdentifier(const QString &str)
+{
+ auto isNonDigit = [](QChar c) -> bool {
+ return (c >= QLatin1Char('a') && c <= QLatin1Char('z')) ||
+ (c >= QLatin1Char('A') && c <= QLatin1Char('Z')) ||
+ c == QLatin1Char('_');
+ };
- cpp << l("};") << endl
- << endl;
- clazz.classMethods.write(cpp, QStringLiteral(""), QStringLiteral("\n"));
+ Q_ASSERT(!str.isEmpty());
- cpp << clazz.className << l("::") << clazz.className << l("(QObject *parent)") << endl
- << QStringLiteral(" : QScxmlStateMachine(parent)") << endl
- << QStringLiteral(" , data(new Data(*this))") << endl
- << QStringLiteral("{ qRegisterMetaType<%1 *>(); data->init(); }").arg(clazz.className) << endl
- << endl;
- cpp << clazz.className << l("::~") << clazz.className << l("()") << endl
- << l("{ delete data; }") << endl
- << endl;
- foreach (const Method &m, clazz.publicMethods) {
- m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n"), clazz.className);
- cpp << endl;
- }
- if (!clazz.protectedMethods.isEmpty()) {
- cpp << "#define SET_SERVICE_PROP(s, n, fq, sig) \\\n"
- " if (id == data->string(s)) { \\\n"
- " QScxmlInvokableScxml *machine = service ? dynamic_cast<QScxmlInvokableScxml *>(service) : Q_NULLPTR; \\\n"
- " fq *casted = machine ? dynamic_cast<fq*>(machine->stateMachine()) : Q_NULLPTR; \\\n"
- " if (data->n != casted) { \\\n"
- " data->n = casted; \\\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"
- << endl;
- foreach (const Method &m, clazz.protectedMethods) {
- m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n"), clazz.className);
- cpp << endl;
+ QString mangled;
+ mangled.reserve(str.size());
+
+ int i = 0;
+ if (str.startsWith(QLatin1Char('_')) && str.size() > 1) {
+ QChar ch = str.at(1);
+ if (ch == QLatin1Char('_')
+ || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) {
+ mangled += QLatin1String("_0x5f_");
+ ++i;
}
- cpp << QStringLiteral("#undef SET_SERVICE_PROP") << endl
- << endl;
}
- clazz.publicSlotDefinitions.write(cpp, QStringLiteral("\n"), QStringLiteral("\n"));
- cpp << endl;
- clazz.tables.write(cpp, QStringLiteral(""), QStringLiteral("\n"));
+ for (int ei = str.length(); i != ei; ++i) {
+ auto c = str.at(i).unicode();
+ if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || isNonDigit(c)) {
+ mangled += c;
+ } else {
+ mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_');
+ }
+ }
- if (!clazz.dataModelMethods.isEmpty()) {
- bool first = true;
- foreach (const Method &m, clazz.dataModelMethods) {
- if (first) {
- first = false;
- } else {
- cpp << endl;
- }
- m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n"));
+ while (true) {
+ auto it = m_mangledToOriginal.constFind(mangled);
+ if (it == m_mangledToOriginal.constEnd()) {
+ m_mangledToOriginal.insert(mangled, str);
+ break;
+ } else if (it.value() == str) {
+ break;
}
+ mangled += QStringLiteral("_"); // append underscores until we get a unique name
}
- cpp << endl << clazz.metaData;
+ return mangled;
}
-void CppDumper::writeImplEnd()
+QString CppDumper::generateSignalDecls(const GeneratedTableData::MetaDataInfo &info)
{
- if (!m_translationUnit->namespaceName.isEmpty()) {
- cpp << endl
- << QStringLiteral("} // %1 namespace").arg(m_translationUnit->namespaceName) << endl;
+ QString decls;
+
+ foreach (const QString &stateName, info.stateNames) {
+ decls += QStringLiteral(" void %1Changed(bool active);\n")
+ .arg(mangleIdentifier(stateName));
+ }
+
+ foreach (const QString &machineName, info.subStateMachineNames) {
+ decls += QStringLiteral(" void %1Changed(QScxmlStateMachine *stateMachine);\n")
+ .arg(mangleIdentifier(machineName));
+ }
+
+ foreach (const QString &eventName, info.outgoingEvents) {
+ decls += QStringLiteral(" void %1(const QVariant &data);\n")
+ .arg(mangleIdentifier(eventName));
+ }
+
+ return decls;
+}
+
+QString CppDumper::generateSlotDecls(const GeneratedTableData::MetaDataInfo &info)
+{
+ QString decls;
+
+ foreach (const QString &eventName, info.incomingEvents) {
+ if (!DocumentModel::isEventToBeGenerated(eventName))
+ continue;
+
+ decls += QStringLiteral(" void %1(const QVariant &data = QVariant());\n")
+ .arg(mangleIdentifier(eventName));
+ }
+
+ return decls;
+}
+
+QString CppDumper::generateSlotDefs(const QString &className,
+ const GeneratedTableData::MetaDataInfo &info)
+{
+ QString defs;
+
+ foreach (const QString &eventName, info.incomingEvents) {
+ if (!DocumentModel::isEventToBeGenerated(eventName))
+ continue;
+
+ const auto mangledName = mangleIdentifier(eventName);
+ defs += QStringLiteral("void %1::%2(const QVariant &data)\n").arg(className, mangledName);
+ defs += QStringLiteral("{ submitEvent(QStringLiteral(\"%1\"), data); }\n\n").arg(eventName);
}
+
+ return defs;
+}
+
+QString CppDumper::generateGetterDecls(const GeneratedTableData::MetaDataInfo &info)
+{
+ QString decls;
+
+ foreach (const QString &stateName, info.stateNames) {
+ decls += QStringLiteral(" bool %1() const;\n").arg(mangleIdentifier(stateName));
+ }
+
+ foreach (const QString &machineName, info.subStateMachineNames) {
+ decls += QStringLiteral(" QScxmlStateMachine *%1() const;\n")
+ .arg(mangleIdentifier(machineName));
+ }
+
+ return decls;
+}
+
+QString CppDumper::generateGetterDefs(const QString &className,
+ const GeneratedTableData::MetaDataInfo &info)
+{
+ QString defs;
+
+ int stateIndex = 0;
+ foreach (const QString &stateName, info.stateNames) {
+ defs += QStringLiteral("bool %1::%2() const\n").arg(className, mangleIdentifier(stateName));
+ defs += QStringLiteral("{ return isActive(%1); }\n\n").arg(stateIndex);
+ ++stateIndex;
+ }
+
+ int machineIndex = 0;
+ foreach (const QString &machineName, info.subStateMachineNames) {
+ defs += QStringLiteral("QScxmlStateMachine *%1::%2() const\n")
+ .arg(className, mangleIdentifier(machineName));
+ defs += QStringLiteral("{ return subStateMachine(%1); }\n\n").arg(machineIndex);
+ ++machineIndex;
+ }
+
+ return defs;
+}
+
+QString CppDumper::generateMetaObject(const QString &className,
+ const GeneratedTableData::MetaDataInfo &info,
+ bool m_qtMode)
+{
+ ClassDef classDef;
+ classDef.classname = className.toUtf8();
+ classDef.qualified = classDef.classname;
+ classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public);
+ classDef.hasQObject = true;
+
+ // stateNames:
+ int stateIdx = 0;
+ foreach (const QString &stateName, info.stateNames) {
+ if (stateName.isEmpty())
+ continue;
+
+ QByteArray mangledStateName = 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);";
+ }
+ 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 = "isActive(" + QByteArray::number(stateIdx++) + ")";
+ prop.notify = mangledStateName + "Changed";
+ prop.notifyId = classDef.signalList.size() - 1;
+ prop.gspec = PropertyDef::ValueSpec;
+ prop.scriptable = "true";
+ classDef.propertyList << prop;
+ }
+
+ // sub-statemachines:
+ QHash<QByteArray, QByteArray> knownQObjectClasses;
+ knownQObjectClasses.insert(QByteArray("QScxmlStateMachine"), QByteArray());
+ int machineIdx = 0;
+ for (const auto &service : info.subStateMachineNames) {
+ const auto serviceName = service;
+ const QString fqServiceClass = QStringLiteral("QScxmlStateMachine");
+ const QByteArray serviceClass = fqServiceClass.toUtf8();
+ knownQObjectClasses.insert(serviceClass, "");
+
+ const QByteArray mangledServiceName = mangleIdentifier(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 = "subStateMachine(" + QByteArray::number(machineIdx++) + ")";
+ prop.notify = mangledServiceName + "Changed";
+ prop.notifyId = classDef.signalList.size() - 1;
+ prop.gspec = PropertyDef::ValueSpec;
+ prop.scriptable = "true";
+ classDef.propertyList << prop;
+ }
+
+ // Event signals:
+ foreach (const QString &signalName, info.outgoingEvents) {
+ 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;
+ }
+
+ // event slots:
+ foreach (const QString &eventName, info.incomingEvents) {
+ if (!DocumentModel::isEventToBeGenerated(eventName))
+ continue;
+
+ 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;
+
+ 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;
+ }
+
+ foreach (const QString &eventName, info.incomingEvents) {
+ if (!DocumentModel::isEventToBeGenerated(eventName))
+ continue;
+
+ 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;
+ }
+
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ Generator(&classDef, QList<QByteArray>(), knownQObjectClasses,
+ QHash<QByteArray, QByteArray>(), buf).generateCode();
+ buf.close();
+ return QString::fromUtf8(buf.buffer());
}
QT_END_NAMESPACE
diff --git a/tools/qscxmlc/scxmlcppdumper.h b/tools/qscxmlc/scxmlcppdumper.h
index f02bf2f..97011d0 100644
--- a/tools/qscxmlc/scxmlcppdumper.h
+++ b/tools/qscxmlc/scxmlcppdumper.h
@@ -32,13 +32,12 @@
#include "qscxmlglobals.h"
#include <QtScxml/private/qscxmlparser_p.h>
+#include <QtScxml/private/qscxmltabledata_p.h>
#include <QTextStream>
QT_BEGIN_NAMESPACE
-struct ClassDump;
-
struct TranslationUnit
{
TranslationUnit()
@@ -74,13 +73,30 @@ public:
private:
void writeHeaderStart(const QString &headerGuard, const QStringList &forwardDecls);
- void writeClass(const ClassDump &clazz);
+ void writeClass(const QString &className,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
void writeHeaderEnd(const QString &headerGuard, const QStringList &metatypeDecls);
- void writeImplStart(const QVector<ClassDump> &allClazzes);
- void writeImplBody(const ClassDump &clazz);
+ void writeImplStart();
+ void writeImplBody(const QScxmlInternal::GeneratedTableData &table,
+ const QString &className,
+ DocumentModel::ScxmlDocument *doc,
+ const QStringList &factory,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
void writeImplEnd();
+ QString mangleIdentifier(const QString &str);
private:
+ QString generateSignalDecls(const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
+ QString generateSlotDecls(const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
+ QString generateSlotDefs(const QString &className,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
+ QString generateGetterDecls(const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
+ QString generateGetterDefs(const QString &className,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info);
+ QString generateMetaObject(const QString &className,
+ const QScxmlInternal::GeneratedTableData::MetaDataInfo &info,
+ bool m_qtMode);
+
QTextStream &h;
QTextStream &cpp;
@@ -88,6 +104,8 @@ private:
static QLatin1String l (const char *str) { return QLatin1String(str); }
TranslationUnit *m_translationUnit;
+
+ mutable QHash<QString, QString> m_mangledToOriginal;
};
QT_END_NAMESPACE
diff --git a/tools/qscxmlc/templates.qrc b/tools/qscxmlc/templates.qrc
new file mode 100644
index 0000000..6f2ccf6
--- /dev/null
+++ b/tools/qscxmlc/templates.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data.t</file>
+ <file>decl.t</file>
+ <file>cppdatamodel.t</file>
+ </qresource>
+</RCC>