| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtScxml module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include "qscxmltabledata_p.h" | 
| 41 | #include "qscxmlcompiler_p.h" | 
| 42 | #include "qscxmlexecutablecontent_p.h" | 
| 43 |  | 
| 44 | QT_USE_NAMESPACE | 
| 45 |  | 
| 46 | /*! | 
| 47 |     \class QScxmlTableData | 
| 48 |     \since 5.8 | 
| 49 |     \inmodule QtScxml | 
| 50 |     \brief The QScxmlTableData class is used by compiled state machines. | 
| 51 |  | 
| 52 |     QScxmlTableData is the interface to the compiled representation of SCXML | 
| 53 |     state machines. It should only be used internally and by state machines | 
| 54 |     compiled from SCXML documents. | 
| 55 |  */ | 
| 56 |  | 
| 57 | /*! | 
| 58 |     \fn QScxmlTableData::string(QScxmlExecutableContent::StringId id) const | 
| 59 |     Returns a QString for the given \a id. | 
| 60 |  */ | 
| 61 |  | 
| 62 | /*! | 
| 63 |     \fn QScxmlTableData::instructions() const | 
| 64 |     Returns a pointer to the instructions of executable content contained in | 
| 65 |     the state machine. | 
| 66 |  */ | 
| 67 |  | 
| 68 | /*! | 
| 69 |     \fn QScxmlTableData::evaluatorInfo(QScxmlExecutableContent::EvaluatorId evaluatorId) const | 
| 70 |     Returns the QScxmlExecutableContent::EvaluatorInfo object for the given \a evaluatorId. | 
| 71 |  */ | 
| 72 |  | 
| 73 | /*! | 
| 74 |     \fn QScxmlTableData::assignmentInfo(QScxmlExecutableContent::EvaluatorId assignmentId) const | 
| 75 |     Returns the QScxmlExecutableContent::AssignmentInfo object for the given \a assignmentId. | 
| 76 |  */ | 
| 77 |  | 
| 78 | /*! | 
| 79 |     \fn QScxmlTableData::foreachInfo(QScxmlExecutableContent::EvaluatorId foreachId) const | 
| 80 |     Returns the QScxmlExecutableContent::ForeachInfo object for the given \a foreachId. | 
| 81 |  */ | 
| 82 |  | 
| 83 | /*! | 
| 84 |     \fn QScxmlTableData::dataNames(int *count) const | 
| 85 |     Retrieves the string IDs for the names of data items in the data model. The | 
| 86 |     number of strings is saved into \a count and a pointer to an array of | 
| 87 |     string IDs is returned. | 
| 88 |  | 
| 89 |     Returns a pointer to an array of string IDs. | 
| 90 |  */ | 
| 91 |  | 
| 92 | /*! | 
| 93 |     \fn QScxmlTableData::initialSetup() const | 
| 94 |     Initializes the table data. Returns the ID of the container with | 
| 95 |     instructions to be executed when initializing the state machine. | 
| 96 |  */ | 
| 97 |  | 
| 98 | /*! | 
| 99 |     \fn QScxmlTableData::name() const | 
| 100 |     Returns the name of the state machine. | 
| 101 |  */ | 
| 102 |  | 
| 103 | /*! | 
| 104 |     \fn QScxmlTableData::stateMachineTable() const | 
| 105 |     Returns a pointer to the complete state table, expressed as an opaque | 
| 106 |     sequence of integers. | 
| 107 |  */ | 
| 108 |  | 
| 109 | /*! | 
| 110 |     \fn QScxmlTableData::serviceFactory(int id) const | 
| 111 |     Returns the service factory that creates invokable services for the state | 
| 112 |     with the ID \a id. | 
| 113 |  */ | 
| 114 |  | 
| 115 | using namespace QScxmlInternal; | 
| 116 |  | 
| 117 | namespace { | 
| 118 | using namespace QScxmlExecutableContent; | 
| 119 |  | 
| 120 | class TableDataBuilder: public DocumentModel::NodeVisitor | 
| 121 | { | 
| 122 | public: | 
| 123 |     TableDataBuilder(GeneratedTableData &tableData, | 
| 124 |                      GeneratedTableData::MetaDataInfo &metaDataInfo, | 
| 125 |                      GeneratedTableData::DataModelInfo &dataModelInfo, | 
| 126 |                      GeneratedTableData::CreateFactoryId func) | 
| 127 |         : createFactoryId(func) | 
| 128 |         , m_tableData(tableData) | 
| 129 |         , m_dataModelInfo(dataModelInfo) | 
| 130 |         , m_stringTable(tableData.theStrings) | 
| 131 |         , m_instructions(tableData.theInstructions) | 
| 132 |         , m_evaluators(tableData.theEvaluators) | 
| 133 |         , m_assignments(tableData.theAssignments) | 
| 134 |         , m_foreaches(tableData.theForeaches) | 
| 135 |         , m_dataIds(tableData.theDataNameIds) | 
| 136 |         , m_stateNames(metaDataInfo.stateNames) | 
| 137 |  | 
| 138 |     { | 
| 139 |         m_activeSequences.reserve(asize: 4); | 
| 140 |         tableData.theInitialSetup = QScxmlExecutableContent::NoContainer; | 
| 141 |     } | 
| 142 |  | 
| 143 |     void buildTableData(DocumentModel::ScxmlDocument *doc) | 
| 144 |     { | 
| 145 |         m_isCppDataModel = doc->root->dataModel == DocumentModel::Scxml::CppDataModel; | 
| 146 |         m_parents.reserve(asize: 32); | 
| 147 |         m_allTransitions.resize(asize: doc->allTransitions.size()); | 
| 148 |         m_docTransitionIndices.reserve(asize: doc->allTransitions.size()); | 
| 149 |         for (auto *t : qAsConst(t&: doc->allTransitions)) { | 
| 150 |             m_docTransitionIndices.insert(akey: t, avalue: m_docTransitionIndices.size()); | 
| 151 |         } | 
| 152 |         m_docStatesIndices.reserve(asize: doc->allStates.size()); | 
| 153 |         m_transitionsForState.resize(asize: doc->allStates.size()); | 
| 154 |         m_allStates.resize(asize: doc->allStates.size()); | 
| 155 |         for (DocumentModel::AbstractState *s : qAsConst(t&: doc->allStates)) { | 
| 156 |             m_docStatesIndices.insert(akey: s, avalue: m_docStatesIndices.size()); | 
| 157 |         } | 
| 158 |  | 
| 159 |         doc->root->accept(visitor: this); | 
| 160 |         m_stateTable.version = Q_QSCXMLC_OUTPUT_REVISION; | 
| 161 |         generateStateMachineData(); | 
| 162 |  | 
| 163 |         m_tableData.theInstructions.squeeze(); | 
| 164 |     } | 
| 165 |  | 
| 166 |     void generateStateMachineData() | 
| 167 |     { | 
| 168 |         const int tableSize = sizeof(StateTable) / sizeof(qint32); | 
| 169 |         const int stateSize = qint32(sizeof(StateTable::State) / sizeof(qint32)); | 
| 170 |         const int transitionSize = qint32(sizeof(StateTable::Transition) / sizeof(qint32)); | 
| 171 |  | 
| 172 |         m_stateTable.stateOffset = tableSize; | 
| 173 |         m_stateTable.stateCount = m_allStates.size(); | 
| 174 |         m_stateTable.transitionOffset = m_stateTable.stateOffset + | 
| 175 |                 m_stateTable.stateCount * stateSize; | 
| 176 |         m_stateTable.transitionCount = m_allTransitions.size(); | 
| 177 |         m_stateTable.arrayOffset = m_stateTable.transitionOffset + | 
| 178 |                 m_stateTable.transitionCount * transitionSize; | 
| 179 |         m_stateTable.arraySize = m_arrays.size(); | 
| 180 |  | 
| 181 |         const qint32 dataSize = qint32(tableSize) | 
| 182 |                 + (m_allStates.size() * stateSize) | 
| 183 |                 + (m_allTransitions.size() * transitionSize) | 
| 184 |                 + m_arrays.size() | 
| 185 |                 + 1; | 
| 186 |         QVector<qint32> data(dataSize, -1); | 
| 187 |         qint32 *ptr = data.data(); | 
| 188 |  | 
| 189 |         memcpy(dest: ptr, src: &m_stateTable, n: sizeof(m_stateTable)); | 
| 190 |         ptr += tableSize; | 
| 191 |  | 
| 192 |         Q_ASSERT(ptr == data.constData() + m_stateTable.stateOffset); | 
| 193 |         memcpy(dest: ptr, src: m_allStates.constData(), | 
| 194 |                n: sizeof(StateTable::State) * size_t(m_allStates.size())); | 
| 195 |         ptr += stateSize * size_t(m_allStates.size()); | 
| 196 |  | 
| 197 |         Q_ASSERT(ptr == data.constData() + m_stateTable.transitionOffset); | 
| 198 |         memcpy(dest: ptr, src: m_allTransitions.constData(), | 
| 199 |                n: sizeof(StateTable::Transition) * size_t(m_allTransitions.size())); | 
| 200 |         ptr += transitionSize * size_t(m_allTransitions.size()); | 
| 201 |  | 
| 202 |         Q_ASSERT(ptr == data.constData() + m_stateTable.arrayOffset); | 
| 203 |         memcpy(dest: ptr, src: m_arrays.constData(), n: sizeof(qint32) * size_t(m_arrays.size())); | 
| 204 |         ptr += m_arrays.size(); | 
| 205 |  | 
| 206 |         *ptr++ = StateTable::terminator; | 
| 207 |  | 
| 208 |         Q_ASSERT(ptr == data.constData() + dataSize); | 
| 209 |  | 
| 210 |         m_tableData.theStateMachineTable = data; | 
| 211 |     } | 
| 212 |  | 
| 213 | protected: // visitor | 
| 214 |     using NodeVisitor::visit; | 
| 215 |  | 
| 216 |     bool visit(DocumentModel::Scxml *node) override final | 
| 217 |     { | 
| 218 |         setName(node->name); | 
| 219 |  | 
| 220 |         switch (node->dataModel) { | 
| 221 |         case DocumentModel::Scxml::NullDataModel: | 
| 222 |             m_stateTable.dataModel = StateTable::NullDataModel; | 
| 223 |             break; | 
| 224 |         case DocumentModel::Scxml::JSDataModel: | 
| 225 |             m_stateTable.dataModel = StateTable::EcmaScriptDataModel; | 
| 226 |             break; | 
| 227 |         case DocumentModel::Scxml::CppDataModel: | 
| 228 |             m_stateTable.dataModel = StateTable::CppDataModel; | 
| 229 |             break; | 
| 230 |         default: | 
| 231 |             m_stateTable.dataModel = StateTable::InvalidDataModel; | 
| 232 |             break; | 
| 233 |         } | 
| 234 |  | 
| 235 |         switch (node->binding) { | 
| 236 |         case DocumentModel::Scxml::EarlyBinding: | 
| 237 |             m_stateTable.binding = StateTable::EarlyBinding; | 
| 238 |             break; | 
| 239 |         case DocumentModel::Scxml::LateBinding: | 
| 240 |             m_stateTable.binding = StateTable::LateBinding; | 
| 241 |             m_bindLate = true; | 
| 242 |             break; | 
| 243 |         default: | 
| 244 |             Q_UNREACHABLE(); | 
| 245 |         } | 
| 246 |  | 
| 247 |         m_stateTable.name = addString(str: node->name); | 
| 248 |  | 
| 249 |         m_parents.append(t: -1); | 
| 250 |         visit(children: node->children); | 
| 251 |  | 
| 252 |         m_dataElements.append(l: node->dataElements); | 
| 253 |         if (node->script || !m_dataElements.isEmpty() || !node->initialSetup.isEmpty()) { | 
| 254 |             setInitialSetup(startNewSequence()); | 
| 255 |             generate(dataElements: m_dataElements); | 
| 256 |             if (node->script) { | 
| 257 |                 node->script->accept(visitor: this); | 
| 258 |             } | 
| 259 |             visit(sequence: &node->initialSetup); | 
| 260 |             endSequence(); | 
| 261 |         } | 
| 262 |  | 
| 263 |         QVector<DocumentModel::AbstractState *> childStates; | 
| 264 |         for (DocumentModel::StateOrTransition *sot : qAsConst(t&: node->children)) { | 
| 265 |             if (DocumentModel::AbstractState *s = sot->asAbstractState()) { | 
| 266 |                 childStates.append(t: s); | 
| 267 |             } | 
| 268 |         } | 
| 269 |         m_stateTable.childStates = addStates(states: childStates); | 
| 270 |         if (node->initialTransition) { | 
| 271 |             visit(transition: node->initialTransition); | 
| 272 |             const int transitionIndex = m_docTransitionIndices.value(akey: node->initialTransition, adefaultValue: -1); | 
| 273 |             Q_ASSERT(transitionIndex != -1); | 
| 274 |             m_stateTable.initialTransition = transitionIndex; | 
| 275 |         } | 
| 276 |         m_parents.removeLast(); | 
| 277 |  | 
| 278 |         return false; | 
| 279 |     } | 
| 280 |  | 
| 281 |     bool visit(DocumentModel::State *state) override final | 
| 282 |     { | 
| 283 |         m_stateNames.add(s: state->id); | 
| 284 |         const int stateIndex = m_docStatesIndices.value(akey: state, adefaultValue: -1); | 
| 285 |         Q_ASSERT(stateIndex != -1); | 
| 286 |         StateTable::State &newState = m_allStates[stateIndex]; | 
| 287 |         newState.name = addString(str: state->id); | 
| 288 |         newState.parent = currentParent(); | 
| 289 |  | 
| 290 |         switch (state->type) { | 
| 291 |         case DocumentModel::State::Normal: | 
| 292 |             newState.type = StateTable::State::Normal; | 
| 293 |             break; | 
| 294 |         case DocumentModel::State::Parallel: | 
| 295 |             newState.type = StateTable::State::Parallel; | 
| 296 |             break; | 
| 297 |         case DocumentModel::State::Final: | 
| 298 |             newState.type = StateTable::State::Final; | 
| 299 |             newState.doneData = generate(node: state->doneData); | 
| 300 |             break; | 
| 301 |         default: | 
| 302 |             Q_UNREACHABLE(); | 
| 303 |         } | 
| 304 |  | 
| 305 |         m_parents.append(t: stateIndex); | 
| 306 |  | 
| 307 |         if (!state->dataElements.isEmpty()) { | 
| 308 |             if (m_bindLate) { | 
| 309 |                 newState.initInstructions = startNewSequence(); | 
| 310 |                 generate(dataElements: state->dataElements); | 
| 311 |                 endSequence(); | 
| 312 |             } else { | 
| 313 |                 m_dataElements.append(l: state->dataElements); | 
| 314 |             } | 
| 315 |         } | 
| 316 |  | 
| 317 |         newState.entryInstructions = generate(inSequences: state->onEntry); | 
| 318 |         newState.exitInstructions = generate(inSequences: state->onExit); | 
| 319 |         if (!state->invokes.isEmpty()) { | 
| 320 |             QVector<int> factoryIds; | 
| 321 |             for (DocumentModel::Invoke *invoke : qAsConst(t&: state->invokes)) { | 
| 322 |                 auto ctxt = createContext(QStringLiteral("invoke" )); | 
| 323 |                 QVector<QScxmlExecutableContent::StringId> namelist; | 
| 324 |                 for (const QString &name : qAsConst(t&: invoke->namelist)) | 
| 325 |                     namelist += addString(str: name); | 
| 326 |                 QVector<QScxmlExecutableContent::ParameterInfo> params; | 
| 327 |                 for (DocumentModel::Param *param : qAsConst(t&: invoke->params)) { | 
| 328 |                     QScxmlExecutableContent::ParameterInfo p; | 
| 329 |                     p.name = addString(str: param->name); | 
| 330 |                     p.expr = createEvaluatorVariant(QStringLiteral("param" ), QStringLiteral("expr" ), | 
| 331 |                                                     expr: param->expr); | 
| 332 |                     p.location = addString(str: param->location); | 
| 333 |                     params.append(t: p); | 
| 334 |                 } | 
| 335 |                 QScxmlExecutableContent::ContainerId finalize = | 
| 336 |                         QScxmlExecutableContent::NoContainer; | 
| 337 |                 if (!invoke->finalize.isEmpty()) { | 
| 338 |                     finalize = startNewSequence(); | 
| 339 |                     visit(sequence: &invoke->finalize); | 
| 340 |                     endSequence(); | 
| 341 |                 } | 
| 342 |                 auto srcexpr = createEvaluatorString(QStringLiteral("invoke" ), | 
| 343 |                                                      QStringLiteral("srcexpr" ), | 
| 344 |                                                      expr: invoke->srcexpr); | 
| 345 |                 QScxmlExecutableContent::InvokeInfo invokeInfo; | 
| 346 |                 invokeInfo.id = addString(str: invoke->id); | 
| 347 |                 invokeInfo.prefix = addString(str: state->id + QStringLiteral(".session-" )); | 
| 348 |                 invokeInfo.location = addString(str: invoke->idLocation); | 
| 349 |                 invokeInfo.context = ctxt; | 
| 350 |                 invokeInfo.expr = srcexpr; | 
| 351 |                 invokeInfo.finalize = finalize; | 
| 352 |                 invokeInfo.autoforward = invoke->autoforward; | 
| 353 |                 const int factoryId = createFactoryId(invokeInfo, namelist, params, | 
| 354 |                                                       invoke->content); | 
| 355 |                 Q_ASSERT(factoryId >= 0); | 
| 356 |                 factoryIds.append(t: factoryId); | 
| 357 |                 m_stateTable.maxServiceId = std::max(a: m_stateTable.maxServiceId, b: factoryId); | 
| 358 |             } | 
| 359 |             newState.serviceFactoryIds = addArray(array: factoryIds); | 
| 360 |         } | 
| 361 |  | 
| 362 |         visit(children: state->children); | 
| 363 |  | 
| 364 |         QVector<DocumentModel::AbstractState *> childStates; | 
| 365 |         for (DocumentModel::StateOrTransition *sot : qAsConst(t&: state->children)) { | 
| 366 |             if (auto s = sot->asAbstractState()) { | 
| 367 |                 childStates.append(t: s); | 
| 368 |             } | 
| 369 |         } | 
| 370 |         newState.childStates = addStates(states: childStates); | 
| 371 |         newState.transitions = addArray(array: m_transitionsForState.at(i: stateIndex)); | 
| 372 |         if (state->initialTransition) { | 
| 373 |             visit(transition: state->initialTransition); | 
| 374 |             newState.initialTransition = m_transitionsForState.at(i: stateIndex).last(); | 
| 375 |         } | 
| 376 |         m_parents.removeLast(); | 
| 377 |  | 
| 378 |         return false; | 
| 379 |     } | 
| 380 |  | 
| 381 |     bool visit(DocumentModel::Transition *transition) override final | 
| 382 |     { | 
| 383 |         const int transitionIndex = m_docTransitionIndices.value(akey: transition, adefaultValue: -1); | 
| 384 |         Q_ASSERT(transitionIndex != -1); | 
| 385 |         StateTable::Transition &newTransition = m_allTransitions[transitionIndex]; | 
| 386 |         const int parentIndex = currentParent(); | 
| 387 |         if (parentIndex != -1) { | 
| 388 |             m_transitionsForState[parentIndex].append(t: transitionIndex); | 
| 389 |         } | 
| 390 |         newTransition.source = parentIndex; | 
| 391 |  | 
| 392 |         if (transition->condition) { | 
| 393 |             newTransition.condition = createEvaluatorBool(QStringLiteral("transition" ), | 
| 394 |                                                           QStringLiteral("cond" ), | 
| 395 |                                                           cond: *transition->condition.data()); | 
| 396 |         } | 
| 397 |  | 
| 398 |         switch (transition->type) { | 
| 399 |         case DocumentModel::Transition::External: | 
| 400 |             newTransition.type = StateTable::Transition::External; | 
| 401 |             break; | 
| 402 |         case DocumentModel::Transition::Internal: | 
| 403 |             newTransition.type = StateTable::Transition::Internal; | 
| 404 |             break; | 
| 405 |         case DocumentModel::Transition::Synthetic: | 
| 406 |             newTransition.type = StateTable::Transition::Synthetic; | 
| 407 |             break; | 
| 408 |         default: | 
| 409 |             Q_UNREACHABLE(); | 
| 410 |         } | 
| 411 |  | 
| 412 |         if (!transition->instructionsOnTransition.isEmpty()) { | 
| 413 |             m_currentTransition = transitionIndex; | 
| 414 |             newTransition.transitionInstructions = startNewSequence(); | 
| 415 |             visit(sequence: &transition->instructionsOnTransition); | 
| 416 |             endSequence(); | 
| 417 |             m_currentTransition = -1; | 
| 418 |         } | 
| 419 |  | 
| 420 |         newTransition.targets = addStates(states: transition->targetStates); | 
| 421 |  | 
| 422 |         QVector<int> eventIds; | 
| 423 |         for (const QString &event : qAsConst(t&: transition->events)) | 
| 424 |             eventIds.push_back(t: addString(str: event)); | 
| 425 |  | 
| 426 |         newTransition.events = addArray(array: eventIds); | 
| 427 |  | 
| 428 |         return false; | 
| 429 |     } | 
| 430 |  | 
| 431 |     bool visit(DocumentModel::HistoryState *historyState) override final | 
| 432 |     { | 
| 433 |         const int stateIndex = m_docStatesIndices.value(akey: historyState, adefaultValue: -1); | 
| 434 |         Q_ASSERT(stateIndex != -1); | 
| 435 |         StateTable::State &newState = m_allStates[stateIndex]; | 
| 436 |         newState.name = addString(str: historyState->id); | 
| 437 |         newState.parent = currentParent(); | 
| 438 |  | 
| 439 |         switch (historyState->type) { | 
| 440 |         case DocumentModel::HistoryState::Shallow: | 
| 441 |             newState.type = StateTable::State::ShallowHistory; | 
| 442 |             break; | 
| 443 |         case DocumentModel::HistoryState::Deep: | 
| 444 |             newState.type = StateTable::State::DeepHistory; | 
| 445 |             break; | 
| 446 |         default: | 
| 447 |             Q_UNREACHABLE(); | 
| 448 |         } | 
| 449 |  | 
| 450 |         m_parents.append(t: stateIndex); | 
| 451 |         visit(children: historyState->children); | 
| 452 |         m_parents.removeLast(); | 
| 453 |         newState.transitions = addArray(array: m_transitionsForState.at(i: stateIndex)); | 
| 454 |         return false; | 
| 455 |     } | 
| 456 |  | 
| 457 |     bool visit(DocumentModel::Send *node) override final | 
| 458 |     { | 
| 459 |         auto instr = m_instructions.add<Send>(extra: Send::calculateExtraSize(paramCount: node->params.size(), | 
| 460 |                                                                        nameCount: node->namelist.size())); | 
| 461 |         instr->instructionLocation = createContext(QStringLiteral("send" )); | 
| 462 |         instr->event = addString(str: node->event); | 
| 463 |         instr->eventexpr = createEvaluatorString(QStringLiteral("send" ), | 
| 464 |                                                  QStringLiteral("eventexpr" ), | 
| 465 |                                                  expr: node->eventexpr); | 
| 466 |         instr->type = addString(str: node->type); | 
| 467 |         instr->typeexpr = createEvaluatorString(QStringLiteral("send" ), | 
| 468 |                                                 QStringLiteral("typeexpr" ), | 
| 469 |                                                 expr: node->typeexpr); | 
| 470 |         instr->target = addString(str: node->target); | 
| 471 |         instr->targetexpr = createEvaluatorString(QStringLiteral("send" ), | 
| 472 |                                                   QStringLiteral("targetexpr" ), | 
| 473 |                                                   expr: node->targetexpr); | 
| 474 |         instr->id = addString(str: node->id); | 
| 475 |         instr->idLocation = addString(str: node->idLocation); | 
| 476 |         instr->delay = addString(str: node->delay); | 
| 477 |         instr->delayexpr = createEvaluatorString(QStringLiteral("send" ), | 
| 478 |                                                  QStringLiteral("delayexpr" ), | 
| 479 |                                                  expr: node->delayexpr); | 
| 480 |         instr->content = addString(str: node->content); | 
| 481 |         instr->contentexpr = createEvaluatorString(QStringLiteral("send" ), | 
| 482 |                                                    QStringLiteral("contentexpr" ), | 
| 483 |                                                    expr: node->contentexpr); | 
| 484 |         generate(out: &instr->namelist, in: node->namelist); | 
| 485 |         generate(out: instr->params(), in: node->params); | 
| 486 |         return false; | 
| 487 |     } | 
| 488 |  | 
| 489 |     void visit(DocumentModel::Raise *node) override final | 
| 490 |     { | 
| 491 |         auto instr = m_instructions.add<Raise>(); | 
| 492 |         instr->event = addString(str: node->event); | 
| 493 |     } | 
| 494 |  | 
| 495 |     void visit(DocumentModel::Log *node) override final | 
| 496 |     { | 
| 497 |         auto instr = m_instructions.add<Log>(); | 
| 498 |         instr->label = addString(str: node->label); | 
| 499 |         instr->expr = createEvaluatorString(QStringLiteral("log" ), | 
| 500 |                                             QStringLiteral("expr" ), | 
| 501 |                                             expr: node->expr); | 
| 502 |     } | 
| 503 |  | 
| 504 |     void visit(DocumentModel::Script *node) override final | 
| 505 |     { | 
| 506 |         auto instr = m_instructions.add<JavaScript>(); | 
| 507 |         instr->go = createEvaluatorVoid(QStringLiteral("script" ), | 
| 508 |                                         QStringLiteral("source" ), | 
| 509 |                                         stuff: node->content); | 
| 510 |     } | 
| 511 |  | 
| 512 |     void visit(DocumentModel::Assign *node) override final | 
| 513 |     { | 
| 514 |         auto instr = m_instructions.add<Assign>(); | 
| 515 |         auto ctxt = createContext(QStringLiteral("assign" ), QStringLiteral("expr" ), attrValue: node->expr); | 
| 516 |         instr->expression = addAssignment(dest: node->location, expr: node->expr, context: ctxt); | 
| 517 |     } | 
| 518 |  | 
| 519 |     bool visit(DocumentModel::If *node) override final | 
| 520 |     { | 
| 521 |         auto instr = m_instructions.add<If>(extra: node->conditions.size()); | 
| 522 |         instr->conditions.count = node->conditions.size(); | 
| 523 |         auto it = instr->conditions.data(); | 
| 524 |         QString tag = QStringLiteral("if" ); | 
| 525 |         for (int i = 0, ei = node->conditions.size(); i != ei; ++i) { | 
| 526 |             *it++ = createEvaluatorBool(instrName: tag, QStringLiteral("cond" ), cond: node->conditions.at(i)); | 
| 527 |             if (i == 0) { | 
| 528 |                 tag = QStringLiteral("elif" ); | 
| 529 |             } | 
| 530 |         } | 
| 531 |         auto outSequences = m_instructions.add<InstructionSequences>(); | 
| 532 |         generate(outSequences, inSequences: node->blocks); | 
| 533 |         return false; | 
| 534 |     } | 
| 535 |  | 
| 536 |     bool visit(DocumentModel::Foreach *node) override final | 
| 537 |     { | 
| 538 |         auto instr = m_instructions.add<Foreach>(); | 
| 539 |         auto ctxt = createContextString(QStringLiteral("foreach" )); | 
| 540 |         instr->doIt = addForeach(array: node->array, item: node->item, index: node->index, context: ctxt); | 
| 541 |         startSequence(sequence: &instr->block); | 
| 542 |         visit(sequence: &node->block); | 
| 543 |         endSequence(); | 
| 544 |         return false; | 
| 545 |     } | 
| 546 |  | 
| 547 |     void visit(DocumentModel::Cancel *node) override final | 
| 548 |     { | 
| 549 |         auto instr = m_instructions.add<Cancel>(); | 
| 550 |         instr->sendid = addString(str: node->sendid); | 
| 551 |         instr->sendidexpr = createEvaluatorString(QStringLiteral("cancel" ), | 
| 552 |                                                   QStringLiteral("sendidexpr" ), | 
| 553 |                                                   expr: node->sendidexpr); | 
| 554 |     } | 
| 555 |  | 
| 556 | protected: | 
| 557 |     static int paramSize() { return sizeof(ParameterInfo) / sizeof(qint32); } | 
| 558 |  | 
| 559 |     ContainerId generate(const DocumentModel::DoneData *node) | 
| 560 |     { | 
| 561 |         auto id = m_instructions.newContainerId(); | 
| 562 |         DoneData *doneData; | 
| 563 |         if (node) { | 
| 564 |             doneData = m_instructions.add<DoneData>(extra: node->params.size() * paramSize()); | 
| 565 |             doneData->contents = addString(str: node->contents); | 
| 566 |             doneData->expr = createEvaluatorString(QStringLiteral("donedata" ), | 
| 567 |                                                    QStringLiteral("expr" ), | 
| 568 |                                                    expr: node->expr); | 
| 569 |             generate(out: &doneData->params, in: node->params); | 
| 570 |         } else { | 
| 571 |             doneData = m_instructions.add<DoneData>(); | 
| 572 |             doneData->contents = NoString; | 
| 573 |             doneData->expr = NoEvaluator; | 
| 574 |             doneData->params.count = 0; | 
| 575 |         } | 
| 576 |         doneData->location = createContext(QStringLiteral("final" )); | 
| 577 |         return id; | 
| 578 |     } | 
| 579 |  | 
| 580 |     StringId createContext(const QString &instrName) | 
| 581 |     { | 
| 582 |         return addString(str: createContextString(instrName)); | 
| 583 |     } | 
| 584 |  | 
| 585 |     void generate(const QVector<DocumentModel::DataElement *> &dataElements) | 
| 586 |     { | 
| 587 |         for (DocumentModel::DataElement *el : dataElements) { | 
| 588 |             auto ctxt = createContext(QStringLiteral("data" ), QStringLiteral("expr" ), attrValue: el->expr); | 
| 589 |             auto evaluator = addDataElement(id: el->id, expr: el->expr, context: ctxt); | 
| 590 |             if (evaluator != NoEvaluator) { | 
| 591 |                 auto instr = m_instructions.add<QScxmlExecutableContent::Initialize>(); | 
| 592 |                 instr->expression = evaluator; | 
| 593 |             } | 
| 594 |         } | 
| 595 |     } | 
| 596 |  | 
| 597 |     ContainerId generate(const DocumentModel::InstructionSequences &inSequences) | 
| 598 |     { | 
| 599 |         if (inSequences.isEmpty()) | 
| 600 |             return NoContainer; | 
| 601 |  | 
| 602 |         auto id = m_instructions.newContainerId(); | 
| 603 |         auto outSequences = m_instructions.add<InstructionSequences>(); | 
| 604 |         generate(outSequences, inSequences); | 
| 605 |         return id; | 
| 606 |     } | 
| 607 |  | 
| 608 |     void generate(Array<ParameterInfo> *out, const QVector<DocumentModel::Param *> &in) | 
| 609 |     { | 
| 610 |         out->count = in.size(); | 
| 611 |         ParameterInfo *it = out->data(); | 
| 612 |         for (DocumentModel::Param *f : in) { | 
| 613 |             it->name = addString(str: f->name); | 
| 614 |             it->expr = createEvaluatorVariant(QStringLiteral("param" ), QStringLiteral("expr" ), | 
| 615 |                                               expr: f->expr); | 
| 616 |             it->location = addString(str: f->location); | 
| 617 |             ++it; | 
| 618 |         } | 
| 619 |     } | 
| 620 |  | 
| 621 |     void generate(InstructionSequences *outSequences, | 
| 622 |                   const DocumentModel::InstructionSequences &inSequences) | 
| 623 |     { | 
| 624 |         int sequencesOffset = m_instructions.offset(instr: outSequences); | 
| 625 |         int sequenceCount = 0; | 
| 626 |         int entryCount = 0; | 
| 627 |         for (DocumentModel::InstructionSequence *sequence : inSequences) { | 
| 628 |             ++sequenceCount; | 
| 629 |             startNewSequence(); | 
| 630 |             visit(sequence); | 
| 631 |             entryCount += endSequence()->size(); | 
| 632 |         } | 
| 633 |         outSequences = m_instructions.at<InstructionSequences>(offset: sequencesOffset); | 
| 634 |         outSequences->sequenceCount = sequenceCount; | 
| 635 |         outSequences->entryCount = entryCount; | 
| 636 |     } | 
| 637 |  | 
| 638 |     void generate(Array<StringId> *out, const QStringList &in) | 
| 639 |     { | 
| 640 |         out->count = in.size(); | 
| 641 |         StringId *it = out->data(); | 
| 642 |         for (const QString &str : in) { | 
| 643 |             *it++ = addString(str); | 
| 644 |         } | 
| 645 |     } | 
| 646 |  | 
| 647 |     ContainerId startNewSequence() | 
| 648 |     { | 
| 649 |         auto id = m_instructions.newContainerId(); | 
| 650 |         auto sequence = m_instructions.add<InstructionSequence>(); | 
| 651 |         startSequence(sequence); | 
| 652 |         return id; | 
| 653 |     } | 
| 654 |  | 
| 655 |     void startSequence(InstructionSequence *sequence) | 
| 656 |     { | 
| 657 |         SequenceInfo info; | 
| 658 |         info.location = m_instructions.offset(instr: sequence); | 
| 659 |         info.entryCount = 0; | 
| 660 |         m_activeSequences.push_back(t: info); | 
| 661 |         m_instructions.setSequenceInfo(&m_activeSequences.last()); | 
| 662 |         sequence->instructionType = Instruction::Sequence; | 
| 663 |         sequence->entryCount = -1; // checked in endSequence | 
| 664 |     } | 
| 665 |  | 
| 666 |     InstructionSequence *endSequence() | 
| 667 |     { | 
| 668 |         SequenceInfo info = m_activeSequences.back(); | 
| 669 |         m_activeSequences.pop_back(); | 
| 670 |         m_instructions.setSequenceInfo(m_activeSequences.isEmpty() ? nullptr : | 
| 671 |                                                                      &m_activeSequences.last()); | 
| 672 |  | 
| 673 |         auto sequence = m_instructions.at<InstructionSequence>(offset: info.location); | 
| 674 |         Q_ASSERT(sequence->entryCount == -1); // set in startSequence | 
| 675 |         sequence->entryCount = info.entryCount; | 
| 676 |         if (!m_activeSequences.isEmpty()) | 
| 677 |             m_activeSequences.last().entryCount += info.entryCount; | 
| 678 |         return sequence; | 
| 679 |     } | 
| 680 |  | 
| 681 |     EvaluatorId createEvaluatorString(const QString &instrName, const QString &attrName, | 
| 682 |                                       const QString &expr) | 
| 683 |     { | 
| 684 |         if (!expr.isEmpty()) { | 
| 685 |             if (isCppDataModel()) { | 
| 686 |                 auto id = m_evaluators.add(s: EvaluatorInfo(), uniqueOnly: false); | 
| 687 |                 m_dataModelInfo.stringEvaluators.insert(akey: id, avalue: expr); | 
| 688 |                 return id; | 
| 689 |             } else { | 
| 690 |                 return addEvaluator(expr, context: createContext(instrName, attrName, attrValue: expr)); | 
| 691 |             } | 
| 692 |         } | 
| 693 |  | 
| 694 |         return NoEvaluator; | 
| 695 |     } | 
| 696 |  | 
| 697 |     EvaluatorId createEvaluatorBool(const QString &instrName, const QString &attrName, | 
| 698 |                                     const QString &cond) | 
| 699 |     { | 
| 700 |         if (!cond.isEmpty()) { | 
| 701 |             if (isCppDataModel()) { | 
| 702 |                 auto id = m_evaluators.add(s: EvaluatorInfo(), uniqueOnly: false); | 
| 703 |                 m_dataModelInfo.boolEvaluators.insert(akey: id, avalue: cond); | 
| 704 |                 return id; | 
| 705 |             } else { | 
| 706 |                 return addEvaluator(expr: cond, context: createContext(instrName, attrName, attrValue: cond)); | 
| 707 |             } | 
| 708 |         } | 
| 709 |  | 
| 710 |         return NoEvaluator; | 
| 711 |     } | 
| 712 |  | 
| 713 |     EvaluatorId createEvaluatorVariant(const QString &instrName, const QString &attrName, | 
| 714 |                                        const QString &expr) | 
| 715 |     { | 
| 716 |         if (!expr.isEmpty()) { | 
| 717 |             if (isCppDataModel()) { | 
| 718 |                 auto id = m_evaluators.add(s: EvaluatorInfo(), uniqueOnly: false); | 
| 719 |                 m_dataModelInfo.variantEvaluators.insert(akey: id, avalue: expr); | 
| 720 |                 return id; | 
| 721 |             } else { | 
| 722 |                 return addEvaluator(expr, context: createContext(instrName, attrName, attrValue: expr)); | 
| 723 |             } | 
| 724 |         } | 
| 725 |  | 
| 726 |         return NoEvaluator; | 
| 727 |     } | 
| 728 |  | 
| 729 |     EvaluatorId createEvaluatorVoid(const QString &instrName, const QString &attrName, | 
| 730 |                                     const QString &stuff) | 
| 731 |     { | 
| 732 |         if (!stuff.isEmpty()) { | 
| 733 |             if (isCppDataModel()) { | 
| 734 |                 auto id = m_evaluators.add(s: EvaluatorInfo(), uniqueOnly: false); | 
| 735 |                 m_dataModelInfo.voidEvaluators.insert(akey: id, avalue: stuff); | 
| 736 |                 return id; | 
| 737 |             } else { | 
| 738 |                 return addEvaluator(expr: stuff, context: createContext(instrName, attrName, attrValue: stuff)); | 
| 739 |             } | 
| 740 |         } | 
| 741 |  | 
| 742 |         return NoEvaluator; | 
| 743 |     } | 
| 744 |  | 
| 745 |     GeneratedTableData *tableData(const QVector<int> &stateMachineTable); | 
| 746 |  | 
| 747 |     StringId addString(const QString &str) | 
| 748 |     { return str.isEmpty() ? NoString : m_stringTable.add(s: str); } | 
| 749 |  | 
| 750 |     void setInitialSetup(ContainerId id) | 
| 751 |     { m_tableData.theInitialSetup = id; } | 
| 752 |  | 
| 753 |     void setName(const QString &name) | 
| 754 |     { m_tableData.theName = addString(str: name); } | 
| 755 |  | 
| 756 |     bool isCppDataModel() const | 
| 757 |     { return m_isCppDataModel; } | 
| 758 |  | 
| 759 |     int addStates(const QVector<DocumentModel::AbstractState *> &states) | 
| 760 |     { | 
| 761 |         QVector<int> array; | 
| 762 |         for (auto *s : states) { | 
| 763 |             int si = m_docStatesIndices.value(akey: s, adefaultValue: -1); | 
| 764 |             Q_ASSERT(si != -1); | 
| 765 |             array.push_back(t: si); | 
| 766 |         } | 
| 767 |  | 
| 768 |         return addArray(array); | 
| 769 |     } | 
| 770 |  | 
| 771 |     int addArray(const QVector<int> &array) | 
| 772 |     { | 
| 773 |         if (array.isEmpty()) | 
| 774 |             return -1; | 
| 775 |  | 
| 776 |         const int res = m_arrays.size(); | 
| 777 |         m_arrays.push_back(t: array.size()); | 
| 778 |         m_arrays.append(l: array); | 
| 779 |         return res; | 
| 780 |     } | 
| 781 |  | 
| 782 |     int currentParent() const | 
| 783 |     { | 
| 784 |         return m_parents.last(); | 
| 785 |     } | 
| 786 |  | 
| 787 |     QString createContextString(const QString &instrName) const | 
| 788 |     { | 
| 789 |         if (m_currentTransition != -1) { | 
| 790 |             QString state; | 
| 791 |             int parent = m_allTransitions.at(i: m_currentTransition).source; | 
| 792 |             if (parent != -1) { | 
| 793 |                 QString parentName = QStringLiteral("(none)" ); | 
| 794 |                 int name = m_allStates.at(i: parent).name; | 
| 795 |                 if (name != -1) { | 
| 796 |                     parentName = m_stringTable.item(pos: name); | 
| 797 |                 } | 
| 798 |                 state = QStringLiteral(" of state '%1'" ).arg(a: parentName); | 
| 799 |             } | 
| 800 |             return QStringLiteral("%1 instruction in transition %3" ).arg(args: instrName, args&: state); | 
| 801 |         } else { | 
| 802 |             QString parentName = QStringLiteral("(none)" ); | 
| 803 |             const int parent = currentParent(); | 
| 804 |             if (parent != -1) { | 
| 805 |                 const int name = m_allStates.at(i: parent).name; | 
| 806 |                 if (name != -1) { | 
| 807 |                     parentName = m_stringTable.item(pos: name); | 
| 808 |                 } | 
| 809 |             } | 
| 810 |             return QStringLiteral("%1 instruction in state %2" ).arg(args: instrName, args&: parentName); | 
| 811 |         } | 
| 812 |     } | 
| 813 |  | 
| 814 |     QString createContext(const QString &instrName, const QString &attrName, | 
| 815 |                           const QString &attrValue) const | 
| 816 |     { | 
| 817 |         const QString location = createContextString(instrName); | 
| 818 |         return QStringLiteral("%1 with %2=\"%3\"" ).arg(a1: location, a2: attrName, a3: attrValue); | 
| 819 |     } | 
| 820 |  | 
| 821 |     EvaluatorId addEvaluator(const QString &expr, const QString &context) | 
| 822 |     { | 
| 823 |         EvaluatorInfo ei; | 
| 824 |         ei.expr = addString(str: expr); | 
| 825 |         ei.context = addString(str: context); | 
| 826 |         return m_evaluators.add(s: ei); | 
| 827 |     } | 
| 828 |  | 
| 829 |     EvaluatorId addAssignment(const QString &dest, const QString &expr, const QString &context) | 
| 830 |     { | 
| 831 |         AssignmentInfo ai; | 
| 832 |         ai.dest = addString(str: dest); | 
| 833 |         ai.expr = addString(str: expr); | 
| 834 |         ai.context = addString(str: context); | 
| 835 |         return m_assignments.add(s: ai); | 
| 836 |     } | 
| 837 |  | 
| 838 |     EvaluatorId addForeach(const QString &array, const QString &item, const QString &index, | 
| 839 |                            const QString &context) | 
| 840 |     { | 
| 841 |         ForeachInfo fi; | 
| 842 |         fi.array = addString(str: array); | 
| 843 |         fi.item = addString(str: item); | 
| 844 |         fi.index = addString(str: index); | 
| 845 |         fi.context = addString(str: context); | 
| 846 |         return m_foreaches.add(s: fi); | 
| 847 |     } | 
| 848 |  | 
| 849 |     EvaluatorId addDataElement(const QString &id, const QString &expr, const QString &context) | 
| 850 |     { | 
| 851 |         auto str = addString(str: id); | 
| 852 |         if (!m_dataIds.contains(t: str)) | 
| 853 |             m_dataIds.append(t: str); | 
| 854 |         if (expr.isEmpty()) | 
| 855 |             return NoEvaluator; | 
| 856 |  | 
| 857 |         return addAssignment(dest: id, expr, context); | 
| 858 |     } | 
| 859 |  | 
| 860 | private: | 
| 861 |     template <class Container, typename T, typename U> | 
| 862 |     class Table { | 
| 863 |         Container &elements; | 
| 864 |         QMap<T, int> indexForElement; | 
| 865 |  | 
| 866 |     public: | 
| 867 |         Table(Container &storage) | 
| 868 |             : elements(storage) | 
| 869 |         {} | 
| 870 |  | 
| 871 |         U add(const T &s, bool uniqueOnly = true) { | 
| 872 |             int pos = uniqueOnly ? indexForElement.value(s, -1) : -1; | 
| 873 |             if (pos == -1) { | 
| 874 |                 pos = elements.size(); | 
| 875 |                 elements.append(s); | 
| 876 |                 indexForElement.insert(s, pos); | 
| 877 |             } | 
| 878 |             return pos; | 
| 879 |         } | 
| 880 |  | 
| 881 |         Container data() { | 
| 882 |             return elements; | 
| 883 |         } | 
| 884 |  | 
| 885 |         const T &item(U pos) const { | 
| 886 |             return elements.at(pos); | 
| 887 |         } | 
| 888 |     }; | 
| 889 |  | 
| 890 |     struct SequenceInfo { | 
| 891 |         int location; | 
| 892 |         qint32 entryCount; // the amount of qint32's that the instructions take up | 
| 893 |     }; | 
| 894 |  | 
| 895 |     class InstructionStorage { | 
| 896 |     public: | 
| 897 |         InstructionStorage(QVector<qint32> &storage) | 
| 898 |             : m_instr(storage) | 
| 899 |             , m_info(nullptr) | 
| 900 |         {} | 
| 901 |  | 
| 902 |         ContainerId newContainerId() const { return m_instr.size(); } | 
| 903 |  | 
| 904 |         template <typename T> | 
| 905 |         T *add(int  = 0) | 
| 906 |         { | 
| 907 |             const int pos = m_instr.size(); | 
| 908 |             const int size = sizeof(T) / sizeof(qint32) + extra; | 
| 909 |             if (m_info) | 
| 910 |                 m_info->entryCount += size; | 
| 911 |             m_instr.resize(asize: pos + size); | 
| 912 |             T *instr = at<T>(pos); | 
| 913 |             Q_ASSERT(instr->instructionType == 0); | 
| 914 |             instr->instructionType = T::kind(); | 
| 915 |             return instr; | 
| 916 |         } | 
| 917 |  | 
| 918 |         int offset(Instruction *instr) const | 
| 919 |         { | 
| 920 |             return reinterpret_cast<qint32 *>(instr) - m_instr.data(); | 
| 921 |         } | 
| 922 |  | 
| 923 |         template <typename T> | 
| 924 |         T *at(int offset) | 
| 925 |         { | 
| 926 |             return reinterpret_cast<T *>(&m_instr[offset]); | 
| 927 |         } | 
| 928 |  | 
| 929 |         void setSequenceInfo(SequenceInfo *info) | 
| 930 |         { | 
| 931 |             m_info = info; | 
| 932 |         } | 
| 933 |  | 
| 934 |     private: | 
| 935 |         QVector<qint32> &m_instr; | 
| 936 |         SequenceInfo *m_info; | 
| 937 |     }; | 
| 938 |  | 
| 939 |     QVector<SequenceInfo> m_activeSequences; | 
| 940 |  | 
| 941 |     GeneratedTableData::CreateFactoryId createFactoryId; | 
| 942 |     GeneratedTableData &m_tableData; | 
| 943 |     GeneratedTableData::DataModelInfo &m_dataModelInfo; | 
| 944 |     Table<QStringList, QString, StringId> m_stringTable; | 
| 945 |     InstructionStorage m_instructions; | 
| 946 |     Table<QVector<EvaluatorInfo>, EvaluatorInfo, EvaluatorId> m_evaluators; | 
| 947 |     Table<QVector<AssignmentInfo>, AssignmentInfo, EvaluatorId> m_assignments; | 
| 948 |     Table<QVector<ForeachInfo>, ForeachInfo, EvaluatorId> m_foreaches; | 
| 949 |     QVector<StringId> &m_dataIds; | 
| 950 |     bool m_isCppDataModel = false; | 
| 951 |  | 
| 952 |     StateTable m_stateTable; | 
| 953 |     QVector<int> m_parents; | 
| 954 |     QVector<qint32> m_arrays; | 
| 955 |  | 
| 956 |     QVector<StateTable::Transition> m_allTransitions; | 
| 957 |     QHash<DocumentModel::Transition *, int> m_docTransitionIndices; | 
| 958 |     QVector<StateTable::State> m_allStates; | 
| 959 |     QHash<DocumentModel::AbstractState *, int> m_docStatesIndices; | 
| 960 |     QVector<QVector<int>> m_transitionsForState; | 
| 961 |  | 
| 962 |     int m_currentTransition = StateTable::InvalidIndex; | 
| 963 |     bool m_bindLate = false; | 
| 964 |     QVector<DocumentModel::DataElement *> m_dataElements; | 
| 965 |     Table<QStringList, QString, int> m_stateNames; | 
| 966 | }; | 
| 967 |  | 
| 968 | } // anonymous namespace | 
| 969 |  | 
| 970 | /*! | 
| 971 |     \fn QScxmlTableData::~QScxmlTableData() | 
| 972 |     Destroys the SXCML table data. | 
| 973 |  */ | 
| 974 | QScxmlTableData::~QScxmlTableData() | 
| 975 | {} | 
| 976 |  | 
| 977 | void GeneratedTableData::build(DocumentModel::ScxmlDocument *doc, | 
| 978 |                                GeneratedTableData *table, | 
| 979 |                                MetaDataInfo *metaDataInfo, | 
| 980 |                                DataModelInfo *dataModelInfo, | 
| 981 |                                GeneratedTableData::CreateFactoryId func) | 
| 982 | { | 
| 983 |     TableDataBuilder builder(*table, *metaDataInfo, *dataModelInfo, func); | 
| 984 |     builder.buildTableData(doc); | 
| 985 | } | 
| 986 |  | 
| 987 | QString GeneratedTableData::toString(const int *stateMachineTable) | 
| 988 | { | 
| 989 |     QString result; | 
| 990 |     QTextStream out(&result); | 
| 991 |  | 
| 992 |     const StateTable *st = reinterpret_cast<const StateTable *>(stateMachineTable); | 
| 993 |  | 
| 994 |     out << "{"  << Qt::endl | 
| 995 |         << "\t0x"  << Qt::hex << st->version << Qt::dec << ", // version"  << Qt::endl | 
| 996 |         << "\t"  << st->name << ", // name"  << Qt::endl | 
| 997 |         << "\t"  << st->dataModel << ", // data-model"  << Qt::endl | 
| 998 |         << "\t"  << st->childStates << ", // child states array offset"  << Qt::endl | 
| 999 |         << "\t"  << st->initialTransition << ", // transition to initial states"  << Qt::endl | 
| 1000 |         << "\t"  << st->initialSetup << ", // initial setup"  << Qt::endl | 
| 1001 |         << "\t"  << st->binding << ", // binding"  << Qt::endl | 
| 1002 |         << "\t"  << st->maxServiceId << ", // maxServiceId"  << Qt::endl | 
| 1003 |         << "\t"  << st->stateOffset << ", "  << st->stateCount | 
| 1004 |                                            << ", // state offset and count"  << Qt::endl | 
| 1005 |         << "\t"  << st->transitionOffset << ", "  << st->transitionCount | 
| 1006 |                                                 << ", // transition offset and count"  << Qt::endl | 
| 1007 |         << "\t"  << st->arrayOffset << ", "  << st->arraySize << ", // array offset and size"  << Qt::endl | 
| 1008 |         << Qt::endl; | 
| 1009 |  | 
| 1010 |     out << "\t// States:"  << Qt::endl; | 
| 1011 |     for (int i = 0; i < st->stateCount; ++i) { | 
| 1012 |         const StateTable::State &s = st->state(idx: i); | 
| 1013 |         out << "\t"  | 
| 1014 |             << s.name << ", "  | 
| 1015 |             << s.parent << ", "  | 
| 1016 |             << s.type << ", "  | 
| 1017 |             << s.initialTransition << ", "  | 
| 1018 |             << s.initInstructions << ", "  | 
| 1019 |             << s.entryInstructions << ", "  | 
| 1020 |             << s.exitInstructions << ", "  | 
| 1021 |             << s.doneData << ", "  | 
| 1022 |             << s.childStates << ", "  | 
| 1023 |             << s.transitions << ", "  | 
| 1024 |             << s.serviceFactoryIds << ","  | 
| 1025 |             << Qt::endl; | 
| 1026 |     } | 
| 1027 |  | 
| 1028 |     out << Qt::endl | 
| 1029 |         << "\t// Transitions:"  << Qt::endl; | 
| 1030 |     for (int i = 0; i < st->transitionCount; ++i) { | 
| 1031 |         auto t = st->transition(idx: i); | 
| 1032 |         out << "\t"  | 
| 1033 |             << t.events << ", "  | 
| 1034 |             << t.condition << ", "  | 
| 1035 |             << t.type << ", "  | 
| 1036 |             << t.source << ", "  | 
| 1037 |             << t.targets << ", "  | 
| 1038 |             << t.transitionInstructions << ", "  | 
| 1039 |             << Qt::endl ; | 
| 1040 |     } | 
| 1041 |  | 
| 1042 |     out << Qt::endl | 
| 1043 |         << "\t// Arrays:"  << Qt::endl; | 
| 1044 |     int nextStart = 0; | 
| 1045 |     while (nextStart < st->arraySize) { | 
| 1046 |         const StateTable::Array a = st->array(idx: nextStart); | 
| 1047 |         out << "\t"  << a.size() << ", " ; | 
| 1048 |         for (int j = 0; j < a.size(); ++j) { | 
| 1049 |             out << a[j] << ", " ; | 
| 1050 |         } | 
| 1051 |         out << Qt::endl; | 
| 1052 |         nextStart += a.size() + 1; | 
| 1053 |     } | 
| 1054 |  | 
| 1055 |     out << Qt::hex; | 
| 1056 |     out << Qt::endl | 
| 1057 |         << "\t0x"  << StateTable::terminator << " // terminator"  << Qt::endl | 
| 1058 |         << "}" ; | 
| 1059 |  | 
| 1060 |     return result; | 
| 1061 | } | 
| 1062 |  | 
| 1063 | QString GeneratedTableData::string(StringId id) const | 
| 1064 | { | 
| 1065 |     return id == NoString ? QString() : theStrings.at(i: id); | 
| 1066 | } | 
| 1067 |  | 
| 1068 | InstructionId *GeneratedTableData::instructions() const | 
| 1069 | { | 
| 1070 |     return const_cast<InstructionId *>(theInstructions.data()); | 
| 1071 | } | 
| 1072 |  | 
| 1073 | EvaluatorInfo GeneratedTableData::evaluatorInfo(EvaluatorId evaluatorId) const | 
| 1074 | { | 
| 1075 |     return theEvaluators[evaluatorId]; | 
| 1076 | } | 
| 1077 |  | 
| 1078 | AssignmentInfo GeneratedTableData::assignmentInfo(EvaluatorId assignmentId) const | 
| 1079 | { | 
| 1080 |     return theAssignments[assignmentId]; | 
| 1081 | } | 
| 1082 |  | 
| 1083 | ForeachInfo GeneratedTableData::foreachInfo(EvaluatorId foreachId) const | 
| 1084 | { | 
| 1085 |     return theForeaches[foreachId]; | 
| 1086 | } | 
| 1087 |  | 
| 1088 | StringId *GeneratedTableData::dataNames(int *count) const | 
| 1089 | { | 
| 1090 |     Q_ASSERT(count); | 
| 1091 |     *count = theDataNameIds.size(); | 
| 1092 |     return const_cast<StringId *>(theDataNameIds.data()); | 
| 1093 | } | 
| 1094 |  | 
| 1095 | ContainerId GeneratedTableData::initialSetup() const | 
| 1096 | { | 
| 1097 |     return theInitialSetup; | 
| 1098 | } | 
| 1099 |  | 
| 1100 | QString GeneratedTableData::name() const | 
| 1101 | { | 
| 1102 |     return string(id: theName); | 
| 1103 | } | 
| 1104 |  | 
| 1105 | const qint32 *GeneratedTableData::stateMachineTable() const | 
| 1106 | { | 
| 1107 |     return theStateMachineTable.constData(); | 
| 1108 | } | 
| 1109 |  | 
| 1110 | QScxmlInvokableServiceFactory *GeneratedTableData::serviceFactory(int id) const | 
| 1111 | { | 
| 1112 |     Q_UNUSED(id); | 
| 1113 |     return nullptr; | 
| 1114 | } | 
| 1115 |  |