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#ifndef QSCXMLEXECUTABLECONTENT_P_H
5#define QSCXMLEXECUTABLECONTENT_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtScxml/qscxmlexecutablecontent.h>
19#include <QtScxml/private/qscxmltabledata_p.h>
20#include <QtScxml/private/qscxmlcompiler_p.h>
21#include <QtCore/qtextstream.h>
22
23#ifndef BUILD_QSCXMLC
24#include <QtScxml/qscxmldatamodel.h>
25#include <QtScxml/qscxmlstatemachine.h>
26#endif // BUILD_QSCXMLC
27
28QT_BEGIN_NAMESPACE
29
30namespace QScxmlExecutableContent {
31
32static inline bool operator<(const EvaluatorInfo &ei1, const EvaluatorInfo &ei2)
33{
34 if (ei1.expr != ei2.expr)
35 return ei1.expr < ei2.expr;
36 else
37 return ei1.context < ei2.context;
38}
39
40static inline bool operator<(const AssignmentInfo &ai1, const AssignmentInfo &ai2)
41{
42 if (ai1.dest != ai2.dest)
43 return ai1.dest < ai2.dest;
44 else if (ai1.expr != ai2.expr)
45 return ai1.expr < ai2.expr;
46 else
47 return ai1.context < ai2.context;
48}
49
50static inline bool operator<(const ForeachInfo &fi1, const ForeachInfo &fi2)
51{
52 if (fi1.array != fi2.array) return fi1.array < fi2.array;
53 if (fi1.item != fi2.item) return fi1.item < fi2.item;
54 if (fi1.index != fi2.index) return fi1.index < fi2.index;
55 return fi1.context < fi2.context;
56}
57
58#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
59#pragma pack(push, 4) // 4 == sizeof(qint32)
60#endif
61
62template <typename T>
63struct Array
64{
65 qint32 count;
66 // T[] data;
67 T *data() { return const_cast<T *>(const_data()); }
68 const T *const_data() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + sizeof(Array<T>)); }
69
70 const T &at(int pos) const { return *(const_data() + pos); }
71 int dataSize() const { return count * sizeof(T) / sizeof(qint32); }
72 int size() const { return sizeof(Array<T>) / sizeof(qint32) + dataSize(); }
73};
74
75struct Q_SCXML_EXPORT Instruction
76{
77 enum InstructionType: qint32 {
78 Sequence = 1,
79 Sequences,
80 Send,
81 Raise,
82 Log,
83 JavaScript,
84 Assign,
85 Initialize,
86 If,
87 Foreach,
88 Cancel,
89 DoneData
90 } instructionType;
91};
92
93struct Q_SCXML_EXPORT DoneData: Instruction
94{
95 StringId location;
96 StringId contents;
97 EvaluatorId expr;
98 Array<ParameterInfo> params;
99
100 static InstructionType kind() { return Instruction::DoneData; }
101};
102
103struct Q_SCXML_EXPORT InstructionSequence: Instruction
104{
105 qint32 entryCount; // the amount of qint32's that the instructions take up
106 // Instruction[] instructions;
107
108 static InstructionType kind() { return Instruction::Sequence; }
109 const InstructionId *instructions() const
110 {
111 return reinterpret_cast<const InstructionId *>(this)
112 + sizeof(InstructionSequence) / sizeof(qint32);
113 }
114 int size() const { return sizeof(InstructionSequence) / sizeof(qint32) + entryCount; }
115};
116
117struct Q_SCXML_EXPORT InstructionSequences: Instruction
118{
119 qint32 sequenceCount;
120 qint32 entryCount; // the amount of qint32's that the sequences take up
121 // InstructionSequence[] sequences;
122
123 static InstructionType kind() { return Instruction::Sequences; }
124 const InstructionSequence *sequences() const {
125 return reinterpret_cast<const InstructionSequence *>(
126 reinterpret_cast<const InstructionId *>(this)
127 + sizeof(InstructionSequences) / sizeof(qint32));
128 }
129 int size() const { return sizeof(InstructionSequences)/sizeof(qint32) + entryCount; }
130 const InstructionId *at(int pos) const
131 {
132 const InstructionId *seq = reinterpret_cast<const InstructionId *>(sequences());
133 while (pos--) {
134 seq += reinterpret_cast<const InstructionSequence *>(seq)->size();
135 }
136 return seq;
137 }
138};
139
140struct Q_SCXML_EXPORT Send: Instruction
141{
142 StringId instructionLocation;
143 StringId event;
144 EvaluatorId eventexpr;
145 StringId type;
146 EvaluatorId typeexpr;
147 StringId target;
148 EvaluatorId targetexpr;
149 StringId id;
150 StringId idLocation;
151 StringId delay;
152 EvaluatorId delayexpr;
153 StringId content;
154 EvaluatorId contentexpr;
155 Array<StringId> namelist;
156// Array<Param> params;
157
158 static InstructionType kind() { return Instruction::Send; }
159
160 int paramsOffset() const
161 {
162 return sizeof(Send) / sizeof(qint32) + namelist.dataSize();
163 }
164
165 int size() const
166 {
167 return paramsOffset() + params()->size();
168 }
169
170 const Array<ParameterInfo> *params() const {
171 return reinterpret_cast<const Array<ParameterInfo> *>(
172 reinterpret_cast<const InstructionId *>(this) + paramsOffset());
173 }
174
175 Array<ParameterInfo> *params() {
176 return reinterpret_cast<Array<ParameterInfo> *>(
177 reinterpret_cast<InstructionId *>(this) + paramsOffset());
178 }
179
180 static int calculateExtraSize(int paramCount, int nameCount) {
181 return 1 + paramCount * sizeof(ParameterInfo) / sizeof(qint32)
182 + nameCount * sizeof(StringId) / sizeof(qint32);
183 }
184};
185
186struct Q_SCXML_EXPORT Raise: Instruction
187{
188 StringId event;
189
190 static InstructionType kind() { return Instruction::Raise; }
191 int size() const { return sizeof(Raise) / sizeof(qint32); }
192};
193
194struct Q_SCXML_EXPORT Log: Instruction
195{
196 StringId label;
197 EvaluatorId expr;
198
199 static InstructionType kind() { return Instruction::Log; }
200 int size() const { return sizeof(Log) / sizeof(qint32); }
201};
202
203struct Q_SCXML_EXPORT JavaScript: Instruction
204{
205 EvaluatorId go;
206
207 static InstructionType kind() { return Instruction::JavaScript; }
208 int size() const { return sizeof(JavaScript) / sizeof(qint32); }
209};
210
211struct Q_SCXML_EXPORT Assign: Instruction
212{
213 EvaluatorId expression;
214
215 static InstructionType kind() { return Instruction::Assign; }
216 int size() const { return sizeof(Assign) / sizeof(qint32); }
217};
218
219struct Q_SCXML_EXPORT Initialize: Instruction
220{
221 EvaluatorId expression;
222
223 static InstructionType kind() { return Instruction::Initialize; }
224 int size() const { return sizeof(Initialize) / sizeof(qint32); }
225};
226
227struct Q_SCXML_EXPORT If: Instruction
228{
229 Array<EvaluatorId> conditions;
230 // InstructionSequences blocks;
231 const InstructionSequences *blocks() const {
232 return reinterpret_cast<const InstructionSequences *>(
233 reinterpret_cast<const InstructionId *>(this) + sizeof(If) / sizeof(qint32)
234 + conditions.dataSize());
235 }
236
237 static InstructionType kind() { return Instruction::If; }
238 int size() const
239 {
240 return sizeof(If) / sizeof(qint32) + blocks()->size() + conditions.dataSize();
241 }
242};
243
244struct Q_SCXML_EXPORT Foreach: Instruction
245{
246 EvaluatorId doIt;
247 InstructionSequence block;
248
249 static InstructionType kind() { return Instruction::Foreach; }
250 int size() const { return sizeof(Foreach) / sizeof(qint32) + block.entryCount; }
251 const InstructionId *blockstart() const
252 {
253 return reinterpret_cast<const InstructionId *>(&block);
254 }
255};
256
257struct Q_SCXML_EXPORT Cancel: Instruction
258{
259 StringId sendid;
260 EvaluatorId sendidexpr;
261
262 static InstructionType kind() { return Instruction::Cancel; }
263 int size() const { return sizeof(Cancel) / sizeof(qint32); }
264};
265
266struct StateTable {
267 int version;
268 int name;
269 enum: int {
270 InvalidDataModel = -1,
271 NullDataModel = 0,
272 EcmaScriptDataModel = 1,
273 CppDataModel = 2
274 } dataModel;
275 int childStates; // offset into offsets
276 int initialTransition;
277 int initialSetup;
278 enum: int { InvalidBinding = -1, EarlyBinding = 0, LateBinding = 1 } binding;
279 int maxServiceId;
280 int stateOffset, stateCount;
281 int transitionOffset, transitionCount;
282 int arrayOffset, arraySize;
283
284 enum { terminator = 0xc0ff33 };
285 enum { InvalidIndex = -1 };
286
287 struct State {
288 int name;
289 int parent;
290 enum: int {
291 Invalid = -1,
292 Normal = 0,
293 Parallel = 1,
294 Final = 2,
295 ShallowHistory = 3,
296 DeepHistory = 4
297 } type;
298 int initialTransition;
299 int initInstructions;
300 int entryInstructions;
301 int exitInstructions;
302 int doneData;
303 int childStates; // offset into arrays
304 int transitions; // offset into arrays
305 int serviceFactoryIds; // offset into arrays
306
307 State()
308 : name(InvalidIndex)
309 , parent(InvalidIndex)
310 , type(Invalid)
311 , initialTransition(InvalidIndex)
312 , initInstructions(InvalidIndex)
313 , entryInstructions(InvalidIndex)
314 , exitInstructions(InvalidIndex)
315 , doneData(InvalidIndex)
316 , childStates(InvalidIndex)
317 , transitions(InvalidIndex)
318 , serviceFactoryIds(InvalidIndex)
319 {}
320
321 bool isAtomic() const
322 { return childStates == InvalidIndex; }
323
324 bool isCompound() const
325 { return type == Normal && childStates != InvalidIndex; }
326
327 bool parentIsScxmlElement() const
328 { return parent == InvalidIndex; }
329
330 bool isHistoryState() const
331 { return type == ShallowHistory || type == DeepHistory; }
332
333 bool isParallel() const
334 { return type == Parallel; }
335 };
336
337 struct Transition {
338 int events; // offset into offsets
339 int condition;
340 enum: int {
341 Invalid = -1,
342 Internal = 0,
343 External = 1,
344 Synthetic = 2
345 } type;
346 int source;
347 int targets; // offset into offsets
348 int transitionInstructions;
349
350 Transition()
351 : events(InvalidIndex)
352 , condition(InvalidIndex)
353 , type(Invalid)
354 , source(InvalidIndex)
355 , targets(InvalidIndex)
356 , transitionInstructions(InvalidIndex)
357 {}
358 };
359
360 struct Array {
361 Array(const int *start): start(start) {}
362 int size() const { return *start; }
363 bool isValid() const { return start != nullptr; }
364
365 int operator[](int idx) const {
366 Q_ASSERT(idx >= 0);
367 Q_ASSERT(idx < size());
368 return *(start + idx + 1);
369 }
370
371 struct const_iterator
372 {
373 const_iterator(const Array &a, int pos): a(a), pos(pos) {}
374
375 // std::iterator_traits
376 using value_type = int;
377 using pointer = const int*;
378 using reference = const int&;
379 using difference_type = std::ptrdiff_t;
380 using iterator_category = std::forward_iterator_tag;
381
382 const_iterator &operator++() {
383 if (pos < a.size()) ++pos;
384 return *this;
385 }
386
387 bool operator==(const const_iterator &other) const
388 { return &other.a == &a && other.pos == pos; }
389
390 bool operator!=(const StateTable::Array::const_iterator &other)
391 { return !this->operator==(other); }
392
393 int operator*() const {
394 if (pos < a.size())
395 return a[pos];
396 else
397 return -1;
398 }
399
400 private:
401 const Array &a;
402 int pos;
403 };
404
405 const_iterator begin() const
406 { return const_iterator(*this, 0); }
407
408 const_iterator end() const
409 { return const_iterator(*this, size()); }
410
411 private:
412 const int *start;
413 };
414
415 StateTable()
416 : version(InvalidIndex)
417 , name(InvalidIndex)
418 , dataModel(InvalidDataModel)
419 , childStates(InvalidIndex)
420 , initialTransition(InvalidIndex)
421 , initialSetup(InvalidIndex)
422 , binding(InvalidBinding)
423 , maxServiceId(InvalidIndex)
424 , stateOffset(InvalidIndex), stateCount(InvalidIndex)
425 , transitionOffset(InvalidIndex), transitionCount(InvalidIndex)
426 , arrayOffset(InvalidIndex), arraySize(InvalidIndex)
427 {}
428
429 const State &state(int idx) const
430 {
431 Q_ASSERT(idx >= 0);
432 Q_ASSERT(idx < stateCount);
433 return reinterpret_cast<const State *>(
434 reinterpret_cast<const int *>(this) + stateOffset)[idx];
435 }
436
437 const Transition &transition(int idx) const
438 {
439 Q_ASSERT(idx >= 0);
440 Q_ASSERT(idx < transitionCount);
441 return reinterpret_cast<const Transition *>(
442 reinterpret_cast<const int *>(this) + transitionOffset)[idx];
443 }
444
445 const Array array(int idx) const
446 {
447 Q_ASSERT(idx < arraySize);
448 if (idx >= 0) {
449 const int *start = reinterpret_cast<const int *>(this) + arrayOffset + idx;
450 Q_ASSERT(*start + idx < arraySize);
451 return Array(start);
452 } else {
453 return Array(nullptr);
454 }
455 }
456};
457
458#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
459#pragma pack(pop)
460#endif
461
462} // QScxmlExecutableContent namespace
463
464class QScxmlExecutionEngine
465{
466 Q_DISABLE_COPY(QScxmlExecutionEngine)
467
468public:
469 QScxmlExecutionEngine(QScxmlStateMachine *stateMachine);
470
471 bool execute(QScxmlExecutableContent::ContainerId ip, const QVariant &extraData = QVariant());
472
473private:
474 const QScxmlExecutableContent::InstructionId *step(
475 const QScxmlExecutableContent::InstructionId *ip, bool *ok);
476
477 QScxmlStateMachine *stateMachine;
478 QVariant extraData;
479};
480
481QT_END_NAMESPACE
482
483#endif // QSCXMLEXECUTABLECONTENT_P_H
484

source code of qtscxml/src/scxml/qscxmlexecutablecontent_p.h