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
44QT_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
115using namespace QScxmlInternal;
116
117namespace {
118using namespace QScxmlExecutableContent;
119
120class TableDataBuilder: public DocumentModel::NodeVisitor
121{
122public:
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
213protected: // 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
556protected:
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
860private:
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 extra = 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 */
974QScxmlTableData::~QScxmlTableData()
975{}
976
977void 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
987QString 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
1063QString GeneratedTableData::string(StringId id) const
1064{
1065 return id == NoString ? QString() : theStrings.at(i: id);
1066}
1067
1068InstructionId *GeneratedTableData::instructions() const
1069{
1070 return const_cast<InstructionId *>(theInstructions.data());
1071}
1072
1073EvaluatorInfo GeneratedTableData::evaluatorInfo(EvaluatorId evaluatorId) const
1074{
1075 return theEvaluators[evaluatorId];
1076}
1077
1078AssignmentInfo GeneratedTableData::assignmentInfo(EvaluatorId assignmentId) const
1079{
1080 return theAssignments[assignmentId];
1081}
1082
1083ForeachInfo GeneratedTableData::foreachInfo(EvaluatorId foreachId) const
1084{
1085 return theForeaches[foreachId];
1086}
1087
1088StringId *GeneratedTableData::dataNames(int *count) const
1089{
1090 Q_ASSERT(count);
1091 *count = theDataNameIds.size();
1092 return const_cast<StringId *>(theDataNameIds.data());
1093}
1094
1095ContainerId GeneratedTableData::initialSetup() const
1096{
1097 return theInitialSetup;
1098}
1099
1100QString GeneratedTableData::name() const
1101{
1102 return string(id: theName);
1103}
1104
1105const qint32 *GeneratedTableData::stateMachineTable() const
1106{
1107 return theStateMachineTable.constData();
1108}
1109
1110QScxmlInvokableServiceFactory *GeneratedTableData::serviceFactory(int id) const
1111{
1112 Q_UNUSED(id);
1113 return nullptr;
1114}
1115

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