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