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

source code of qtscxml/src/scxml/qscxmltabledata.cpp