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 | |
28 | QT_BEGIN_NAMESPACE |
29 | |
30 | namespace QScxmlExecutableContent { |
31 | |
32 | static 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 | |
40 | static 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 | |
50 | static 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 | |
62 | template <typename T> |
63 | struct 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 | |
75 | struct 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 | |
93 | struct 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 | |
103 | struct 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 | |
117 | struct 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 | |
140 | struct 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 (int paramCount, int nameCount) { |
181 | return 1 + paramCount * sizeof(ParameterInfo) / sizeof(qint32) |
182 | + nameCount * sizeof(StringId) / sizeof(qint32); |
183 | } |
184 | }; |
185 | |
186 | struct 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 | |
194 | struct 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 | |
203 | struct 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 | |
211 | struct 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 | |
219 | struct 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 | |
227 | struct 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 | |
244 | struct 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 | |
257 | struct 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 | |
266 | struct 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 | |
464 | class QScxmlExecutionEngine |
465 | { |
466 | Q_DISABLE_COPY(QScxmlExecutionEngine) |
467 | |
468 | public: |
469 | QScxmlExecutionEngine(QScxmlStateMachine *stateMachine); |
470 | |
471 | bool execute(QScxmlExecutableContent::ContainerId ip, const QVariant & = QVariant()); |
472 | |
473 | private: |
474 | const QScxmlExecutableContent::InstructionId *step( |
475 | const QScxmlExecutableContent::InstructionId *ip, bool *ok); |
476 | |
477 | QScxmlStateMachine *stateMachine; |
478 | QVariant ; |
479 | }; |
480 | |
481 | QT_END_NAMESPACE |
482 | |
483 | #endif // QSCXMLEXECUTABLECONTENT_P_H |
484 | |