summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@theqtcompany.com>2016-06-08 13:33:26 +0200
committerErik Verbruggen <erik.verbruggen@qt.io>2016-06-09 14:13:57 +0000
commit17899954bffd902b61ee4c9d339095cb618e4346 (patch)
tree6d4a74f2c8e41f3760bf9df4ec982af48cf2b798
parent60b5508b3f0fabee545e8cfd907ed4875ed55ffc (diff)
Replace the QStateMachine-based implementation.
Under the 'hood a table-based state machine is now used instead of the QStateMachine. The advantage is that states and transitions are no longer QObjects but integers (so no (private) object allocations), and we're no longer hijacking the QStateMachinePrivate to work around some implementation details. Change-Id: I47ab47ab01cbb2204b9ca0e4cdd6a72faf724ce3 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
-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>