// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qscxmltabledata_p.h" #include "qscxmlcompiler_p.h" #include "qscxmlexecutablecontent_p.h" #include QT_USE_NAMESPACE /*! \class QScxmlTableData \since 5.8 \inmodule QtScxml \brief The QScxmlTableData class is used by compiled state machines. QScxmlTableData is the interface to the compiled representation of SCXML state machines. It should only be used internally and by state machines compiled from SCXML documents. */ /*! \fn QScxmlTableData::string(QScxmlExecutableContent::StringId id) const Returns a QString for the given \a id. */ /*! \fn QScxmlTableData::instructions() const Returns a pointer to the instructions of executable content contained in the state machine. */ /*! \fn QScxmlTableData::evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const Returns the QScxmlExecutableContent::EvaluatorInfo object for the given \a evaluatorId. */ /*! \fn QScxmlTableData::assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const Returns the QScxmlExecutableContent::AssignmentInfo object for the given \a assignmentId. */ /*! \fn QScxmlTableData::foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const Returns the QScxmlExecutableContent::ForeachInfo object for the given \a foreachId. */ /*! \fn QScxmlTableData::dataNames(int *count) const Retrieves the string IDs for the names of data items in the data model. The number of strings is saved into \a count and a pointer to an array of string IDs is returned. Returns a pointer to an array of string IDs. */ /*! \fn QScxmlTableData::initialSetup() const Initializes the table data. Returns the ID of the container with instructions to be executed when initializing the state machine. */ /*! \fn QScxmlTableData::name() const Returns the name of the state machine. */ /*! \fn QScxmlTableData::stateMachineTable() const Returns a pointer to the complete state table, expressed as an opaque sequence of integers. */ /*! \fn QScxmlTableData::serviceFactory(int id) const Returns the service factory that creates invokable services for the state with the ID \a id. */ 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_stateNames(metaDataInfo.stateNames) { m_activeSequences.reserve(4); tableData.theInitialSetup = QScxmlExecutableContent::NoContainer; } void buildTableData(DocumentModel::ScxmlDocument *doc) { m_isCppDataModel = doc->root->dataModel == DocumentModel::Scxml::CppDataModel; m_parents.reserve(32); m_allTransitions.resize(doc->allTransitions.size()); m_docTransitionIndices.reserve(doc->allTransitions.size()); for (auto *t : std::as_const(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()); for (DocumentModel::AbstractState *s : std::as_const(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; QList 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) override 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(); } QList childStates; for (DocumentModel::StateOrTransition *sot : std::as_const(node->children)) { if (DocumentModel::AbstractState *s = sot->asAbstractState()) { childStates.append(s); } } m_stateTable.childStates = addStates(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) override 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()) { QList factoryIds; for (DocumentModel::Invoke *invoke : std::as_const(state->invokes)) { auto ctxt = createContext(QStringLiteral("invoke")); QList namelist; for (const QString &name : std::as_const(invoke->namelist)) namelist += addString(name); QList params; for (DocumentModel::Param *param : std::as_const(invoke->params)) { QScxmlExecutableContent::ParameterInfo 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::NoContainer; if (!invoke->finalize.isEmpty()) { finalize = startNewSequence(); visit(&invoke->finalize); endSequence(); } auto srcexpr = createEvaluatorString(QStringLiteral("invoke"), QStringLiteral("srcexpr"), invoke->srcexpr); QScxmlExecutableContent::InvokeInfo invokeInfo; invokeInfo.id = addString(invoke->id); invokeInfo.prefix = addString(state->id + QStringLiteral(".session-")); invokeInfo.location = addString(invoke->idLocation); invokeInfo.context = ctxt; invokeInfo.expr = srcexpr; invokeInfo.finalize = finalize; invokeInfo.autoforward = invoke->autoforward; const int factoryId = createFactoryId(invokeInfo, namelist, params, invoke->content); Q_ASSERT(factoryId >= 0); factoryIds.append(factoryId); m_stateTable.maxServiceId = std::max(m_stateTable.maxServiceId, factoryId); } newState.serviceFactoryIds = addArray(factoryIds); } visit(state->children); QList childStates; for (DocumentModel::StateOrTransition *sot : std::as_const(state->children)) { if (auto s = sot->asAbstractState()) { childStates.append(s); } } newState.childStates = addStates(childStates); newState.transitions = addArray(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) override 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; case DocumentModel::Transition::Synthetic: newTransition.type = StateTable::Transition::Synthetic; break; default: Q_UNREACHABLE(); } if (!transition->instructionsOnTransition.isEmpty()) { m_currentTransition = transitionIndex; newTransition.transitionInstructions = startNewSequence(); visit(&transition->instructionsOnTransition); endSequence(); m_currentTransition = -1; } newTransition.targets = addStates(transition->targetStates); QList eventIds; for (const QString &event : std::as_const(transition->events)) eventIds.push_back(addString(event)); newTransition.events = addArray(eventIds); return false; } bool visit(DocumentModel::HistoryState *historyState) override 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(); newState.transitions = addArray(m_transitionsForState.at(stateIndex)); return false; } bool visit(DocumentModel::Send *node) override final { auto instr = m_instructions.add(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); instr->contentexpr = createEvaluatorString(QStringLiteral("send"), QStringLiteral("contentexpr"), node->contentexpr); generate(&instr->namelist, node->namelist); generate(instr->params(), node->params); return false; } void visit(DocumentModel::Raise *node) override final { auto instr = m_instructions.add(); instr->event = addString(node->event); } void visit(DocumentModel::Log *node) override final { auto instr = m_instructions.add(); instr->label = addString(node->label); instr->expr = createEvaluatorString(QStringLiteral("log"), QStringLiteral("expr"), node->expr); } void visit(DocumentModel::Script *node) override final { auto instr = m_instructions.add(); instr->go = createEvaluatorVoid(QStringLiteral("script"), QStringLiteral("source"), node->content); } void visit(DocumentModel::Assign *node) override final { auto instr = m_instructions.add(); auto ctxt = createContext(QStringLiteral("assign"), QStringLiteral("expr"), node->expr); instr->expression = addAssignment(node->location, node->expr, ctxt); } bool visit(DocumentModel::If *node) override final { auto instr = m_instructions.add(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(); generate(outSequences, node->blocks); return false; } bool visit(DocumentModel::Foreach *node) override final { auto instr = m_instructions.add(); 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) override final { auto instr = m_instructions.add(); instr->sendid = addString(node->sendid); instr->sendidexpr = createEvaluatorString(QStringLiteral("cancel"), QStringLiteral("sendidexpr"), node->sendidexpr); } protected: static int paramSize() { return sizeof(ParameterInfo) / sizeof(qint32); } ContainerId generate(const DocumentModel::DoneData *node) { auto id = m_instructions.newContainerId(); DoneData *doneData; if (node) { doneData = m_instructions.add(node->params.size() * paramSize()); 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->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 QList &dataElements) { for (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(); instr->expression = evaluator; } } } ContainerId generate(const DocumentModel::InstructionSequences &inSequences) { if (inSequences.isEmpty()) return NoContainer; auto id = m_instructions.newContainerId(); auto outSequences = m_instructions.add(); generate(outSequences, inSequences); return id; } void generate(Array *out, const QList &in) { out->count = in.size(); ParameterInfo *it = out->data(); for (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; for (DocumentModel::InstructionSequence *sequence : inSequences) { ++sequenceCount; startNewSequence(); visit(sequence); entryCount += endSequence()->size(); } outSequences = m_instructions.at(sequencesOffset); outSequences->sequenceCount = sequenceCount; outSequences->entryCount = entryCount; } void generate(Array *out, const QStringList &in) { out->count = in.size(); StringId *it = out->data(); for (const QString &str : in) { *it++ = addString(str); } } ContainerId startNewSequence() { auto id = m_instructions.newContainerId(); auto sequence = m_instructions.add(); 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() ? nullptr : &m_activeSequences.last()); auto sequence = m_instructions.at(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 QList &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; } int addStates(const QList &states) { QList array; for (auto *s : states) { int si = m_docStatesIndices.value(s, -1); Q_ASSERT(si != -1); array.push_back(si); } return addArray(array); } int addArray(const QList &array) { if (array.isEmpty()) return -1; const int res = m_arrays.size(); m_arrays.push_back(array.size()); m_arrays.append(array); return res; } 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 Table { Container &elements; QMap 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(QList &storage) : m_instr(storage) , m_info(nullptr) {} ContainerId newContainerId() const { return m_instr.size(); } template T *add(int extra = 0) { const int pos = m_instr.size(); const int size = sizeof(T) / sizeof(qint32) + extra; if (m_info) m_info->entryCount += size; m_instr.resize(pos + size); T *instr = at(pos); Q_ASSERT(instr->instructionType == 0); instr->instructionType = T::kind(); return instr; } int offset(Instruction *instr) const { return reinterpret_cast(instr) - m_instr.data(); } template T *at(int offset) { return reinterpret_cast(&m_instr[offset]); } void setSequenceInfo(SequenceInfo *info) { m_info = info; } private: QList &m_instr; SequenceInfo *m_info; }; QList m_activeSequences; GeneratedTableData::CreateFactoryId createFactoryId; GeneratedTableData &m_tableData; GeneratedTableData::DataModelInfo &m_dataModelInfo; Table m_stringTable; InstructionStorage m_instructions; Table, EvaluatorInfo, EvaluatorId> m_evaluators; Table, AssignmentInfo, EvaluatorId> m_assignments; Table, ForeachInfo, EvaluatorId> m_foreaches; QList &m_dataIds; bool m_isCppDataModel = false; StateTable m_stateTable; QList m_parents; QList m_arrays; QList m_allTransitions; QHash m_docTransitionIndices; QList m_allStates; QHash m_docStatesIndices; QList> m_transitionsForState; int m_currentTransition = StateTable::InvalidIndex; bool m_bindLate = false; QList m_dataElements; Table m_stateNames; }; } // anonymous namespace /*! \fn QScxmlTableData::~QScxmlTableData() Destroys the SXCML table data. */ QScxmlTableData::~QScxmlTableData() {} 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(stateMachineTable); out << "{" << Qt::endl << "\t0x" << Qt::hex << st->version << Qt::dec << ", // version" << Qt::endl << "\t" << st->name << ", // name" << Qt::endl << "\t" << st->dataModel << ", // data-model" << Qt::endl << "\t" << st->childStates << ", // child states array offset" << Qt::endl << "\t" << st->initialTransition << ", // transition to initial states" << Qt::endl << "\t" << st->initialSetup << ", // initial setup" << Qt::endl << "\t" << st->binding << ", // binding" << Qt::endl << "\t" << st->maxServiceId << ", // maxServiceId" << Qt::endl << "\t" << st->stateOffset << ", " << st->stateCount << ", // state offset and count" << Qt::endl << "\t" << st->transitionOffset << ", " << st->transitionCount << ", // transition offset and count" << Qt::endl << "\t" << st->arrayOffset << ", " << st->arraySize << ", // array offset and size" << Qt::endl << Qt::endl; out << "\t// States:" << Qt::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 << "," << Qt::endl; } out << Qt::endl << "\t// Transitions:" << Qt::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 << ", " << Qt::endl ; } out << Qt::endl << "\t// Arrays:" << Qt::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 << Qt::endl; nextStart += a.size() + 1; } out << Qt::hex; out << Qt::endl << "\t0x" << StateTable::terminator << " // terminator" << Qt::endl << "}"; return result; } QString GeneratedTableData::string(StringId id) const { return id == NoString ? QString() : theStrings.at(id); } InstructionId *GeneratedTableData::instructions() const { return const_cast(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(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; }