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;
dataArray103     T *data() { return const_cast<T *>(const_data()); }
const_dataArray104     const T *const_data() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + sizeof(Array<T>)); }
105 
atArray106     const T &at(int pos) const { return *(const_data() + pos); }
dataSizeArray107     int dataSize() const { return count * sizeof(T) / sizeof(qint32); }
sizeArray108     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 
kindDoneData136     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 
kindInstructionSequence144     static InstructionType kind() { return Instruction::Sequence; }
instructionsInstructionSequence145     const InstructionId *instructions() const
146     {
147         return reinterpret_cast<const InstructionId *>(this)
148                 + sizeof(InstructionSequence) / sizeof(qint32);
149     }
sizeInstructionSequence150     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 
kindInstructionSequences159     static InstructionType kind() { return Instruction::Sequences; }
sequencesInstructionSequences160     const InstructionSequence *sequences() const {
161         return reinterpret_cast<const InstructionSequence *>(
162                     reinterpret_cast<const InstructionId *>(this)
163                     + sizeof(InstructionSequences) / sizeof(qint32));
164     }
sizeInstructionSequences165     int size() const { return sizeof(InstructionSequences)/sizeof(qint32) + entryCount; }
atInstructionSequences166     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 
kindSend194     static InstructionType kind() { return Instruction::Send; }
195 
paramsOffsetSend196     int paramsOffset() const
197     {
198         return sizeof(Send) / sizeof(qint32) + namelist.dataSize();
199     }
200 
sizeSend201     int size() const
202     {
203         return paramsOffset() + params()->size();
204     }
205 
paramsSend206     const Array<ParameterInfo> *params() const {
207         return reinterpret_cast<const Array<ParameterInfo> *>(
208                     reinterpret_cast<const InstructionId *>(this) + paramsOffset());
209     }
210 
paramsSend211     Array<ParameterInfo> *params() {
212         return reinterpret_cast<Array<ParameterInfo> *>(
213                     reinterpret_cast<InstructionId *>(this) + paramsOffset());
214     }
215 
calculateExtraSizeSend216     static int calculateExtraSize(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 
kindRaise226     static InstructionType kind() { return Instruction::Raise; }
sizeRaise227     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 
kindLog235     static InstructionType kind() { return Instruction::Log; }
sizeLog236     int size() const { return sizeof(Log) / sizeof(qint32); }
237 };
238 
239 struct Q_SCXML_EXPORT JavaScript: Instruction
240 {
241     EvaluatorId go;
242 
kindJavaScript243     static InstructionType kind() { return Instruction::JavaScript; }
sizeJavaScript244     int size() const { return sizeof(JavaScript) / sizeof(qint32); }
245 };
246 
247 struct Q_SCXML_EXPORT Assign: Instruction
248 {
249     EvaluatorId expression;
250 
kindAssign251     static InstructionType kind() { return Instruction::Assign; }
sizeAssign252     int size() const { return sizeof(Assign) / sizeof(qint32); }
253 };
254 
255 struct Q_SCXML_EXPORT Initialize: Instruction
256 {
257     EvaluatorId expression;
258 
kindInitialize259     static InstructionType kind() { return Instruction::Initialize; }
sizeInitialize260     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;
blocksIf267     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 
kindIf273     static InstructionType kind() { return Instruction::If; }
sizeIf274     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 
kindForeach285     static InstructionType kind() { return Instruction::Foreach; }
sizeForeach286     int size() const { return sizeof(Foreach) / sizeof(qint32) + block.entryCount; }
blockstartForeach287     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 
kindCancel298     static InstructionType kind() { return Instruction::Cancel; }
sizeCancel299     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 
StateStateTable::State343         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 
isAtomicStateTable::State357         bool isAtomic() const
358         { return childStates == InvalidIndex; }
359 
isCompoundStateTable::State360         bool isCompound() const
361         { return type == Normal && childStates != InvalidIndex; }
362 
parentIsScxmlElementStateTable::State363         bool parentIsScxmlElement() const
364         { return parent == InvalidIndex; }
365 
isHistoryStateStateTable::State366         bool isHistoryState() const
367         { return type == ShallowHistory || type == DeepHistory; }
368 
isParallelStateTable::State369         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 
TransitionStateTable::Transition386         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 {
ArrayStateTable::Array397         Array(const int *start): start(start) {}
sizeStateTable::Array398         int size() const { return *start; }
isValidStateTable::Array399         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         {
const_iteratorStateTable::Array::const_iterator410             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 
beginStateTable::Array435         const_iterator begin() const
436         { return const_iterator(*this, 0); }
437 
endStateTable::Array438         const_iterator end() const
439         { return const_iterator(*this, size()); }
440 
441     private:
442         const int *start;
443     };
444 
StateTableStateTable445     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 
stateStateTable459     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 
transitionStateTable467     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 
arrayStateTable475     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 &extraData = QVariant());
502 
503 private:
504     const QScxmlExecutableContent::InstructionId *step(
505             const QScxmlExecutableContent::InstructionId *ip, bool *ok);
506 
507     QScxmlStateMachine *stateMachine;
508     QVariant extraData;
509 };
510 
511 QT_END_NAMESPACE
512 
513 #endif // QSCXMLEXECUTABLECONTENT_P_H
514