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