Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #ifndef QSCXMLEXECUTABLECONTENT_P_H
++ #define QSCXMLEXECUTABLECONTENT_P_H
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++
++ #include <QtScxml/qscxmlexecutablecontent.h>
++ #include <QtScxml/private/qscxmltabledata_p.h>
++ #include <QtScxml/private/qscxmlcompiler_p.h>
++ #include <QtCore/qtextstream.h>
++
++ #ifndef BUILD_QSCXMLC
++ #include <QtScxml/qscxmldatamodel.h>
++ #include <QtScxml/qscxmlstatemachine.h>
++ #endif // BUILD_QSCXMLC
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QScxmlExecutableContent {
++
++ static inline bool operator<(const EvaluatorInfo &ei1, const EvaluatorInfo &ei2)
++ {
++ if (ei1.expr != ei2.expr)
++ return ei1.expr < ei2.expr;
++ else
++ return ei1.context < ei2.context;
++ }
++
++ static inline bool operator<(const AssignmentInfo &ai1, const AssignmentInfo &ai2)
++ {
++ if (ai1.dest != ai2.dest)
++ return ai1.dest < ai2.dest;
++ else if (ai1.expr != ai2.expr)
++ return ai1.expr < ai2.expr;
++ else
++ return ai1.context < ai2.context;
++ }
++
++ static inline bool operator<(const ForeachInfo &fi1, const ForeachInfo &fi2)
++ {
++ if (fi1.array != fi2.array) return fi1.array < fi2.array;
++ if (fi1.item != fi2.item) return fi1.item < fi2.item;
++ if (fi1.index != fi2.index) return fi1.index < fi2.index;
++ return fi1.context < fi2.context;
++ }
++
++ #if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
++ #pragma pack(push, 4) // 4 == sizeof(qint32)
++ #endif
++
++ template <typename T>
++ struct Array
++ {
++ qint32 count;
++ // T[] data;
++ T *data() { return const_cast<T *>(const_data()); }
++ const T *const_data() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + sizeof(Array<T>)); }
++
++ const T &at(int pos) const { return *(const_data() + pos); }
++ int dataSize() const { return count * sizeof(T) / sizeof(qint32); }
++ int size() const { return sizeof(Array<T>) / sizeof(qint32) + dataSize(); }
++ };
++
++ struct Q_SCXML_EXPORT Instruction
++ {
++ enum InstructionType: qint32 {
++ Sequence = 1,
++ Sequences,
++ Send,
++ Raise,
++ Log,
++ JavaScript,
++ Assign,
++ Initialize,
++ If,
++ Foreach,
++ Cancel,
++ DoneData
++ } instructionType;
++ };
++
++ struct Q_SCXML_EXPORT DoneData: Instruction
++ {
++ StringId location;
++ StringId contents;
++ EvaluatorId expr;
++ Array<ParameterInfo> params;
++
++ static InstructionType kind() { return Instruction::DoneData; }
++ };
++
++ struct Q_SCXML_EXPORT InstructionSequence: Instruction
++ {
++ qint32 entryCount; // the amount of qint32's that the instructions take up
++ // Instruction[] instructions;
++
++ static InstructionType kind() { return Instruction::Sequence; }
++ const InstructionId *instructions() const
++ {
++ return reinterpret_cast<const InstructionId *>(this)
++ + sizeof(InstructionSequence) / sizeof(qint32);
++ }
++ int size() const { return sizeof(InstructionSequence) / sizeof(qint32) + entryCount; }
++ };
++
++ struct Q_SCXML_EXPORT InstructionSequences: Instruction
++ {
++ qint32 sequenceCount;
++ qint32 entryCount; // the amount of qint32's that the sequences take up
++ // InstructionSequence[] sequences;
++
++ static InstructionType kind() { return Instruction::Sequences; }
++ const InstructionSequence *sequences() const {
++ return reinterpret_cast<const InstructionSequence *>(
++ reinterpret_cast<const InstructionId *>(this)
++ + sizeof(InstructionSequences) / sizeof(qint32));
++ }
++ int size() const { return sizeof(InstructionSequences)/sizeof(qint32) + entryCount; }
++ const InstructionId *at(int pos) const
++ {
++ const InstructionId *seq = reinterpret_cast<const InstructionId *>(sequences());
++ while (pos--) {
++ seq += reinterpret_cast<const InstructionSequence *>(seq)->size();
++ }
++ return seq;
++ }
++ };
++
++ struct Q_SCXML_EXPORT Send: Instruction
++ {
++ StringId instructionLocation;
++ StringId event;
++ EvaluatorId eventexpr;
++ StringId type;
++ EvaluatorId typeexpr;
++ StringId target;
++ EvaluatorId targetexpr;
++ StringId id;
++ StringId idLocation;
++ StringId delay;
++ EvaluatorId delayexpr;
++ StringId content;
++ EvaluatorId contentexpr;
++ Array<StringId> namelist;
++ // Array<Param> params;
++
++ static InstructionType kind() { return Instruction::Send; }
++
++ int paramsOffset() const
++ {
++ return sizeof(Send) / sizeof(qint32) + namelist.dataSize();
++ }
++
++ int size() const
++ {
++ return paramsOffset() + params()->size();
++ }
++
++ const Array<ParameterInfo> *params() const {
++ return reinterpret_cast<const Array<ParameterInfo> *>(
++ reinterpret_cast<const InstructionId *>(this) + paramsOffset());
++ }
++
++ Array<ParameterInfo> *params() {
++ return reinterpret_cast<Array<ParameterInfo> *>(
++ reinterpret_cast<InstructionId *>(this) + paramsOffset());
++ }
++
++ static int calculateExtraSize(int paramCount, int nameCount) {
++ return 1 + paramCount * sizeof(ParameterInfo) / sizeof(qint32)
++ + nameCount * sizeof(StringId) / sizeof(qint32);
++ }
++ };
++
++ struct Q_SCXML_EXPORT Raise: Instruction
++ {
++ StringId event;
++
++ static InstructionType kind() { return Instruction::Raise; }
++ int size() const { return sizeof(Raise) / sizeof(qint32); }
++ };
++
++ struct Q_SCXML_EXPORT Log: Instruction
++ {
++ StringId label;
++ EvaluatorId expr;
++
++ static InstructionType kind() { return Instruction::Log; }
++ int size() const { return sizeof(Log) / sizeof(qint32); }
++ };
++
++ struct Q_SCXML_EXPORT JavaScript: Instruction
++ {
++ EvaluatorId go;
++
++ static InstructionType kind() { return Instruction::JavaScript; }
++ int size() const { return sizeof(JavaScript) / sizeof(qint32); }
++ };
++
++ struct Q_SCXML_EXPORT Assign: Instruction
++ {
++ EvaluatorId expression;
++
++ static InstructionType kind() { return Instruction::Assign; }
++ int size() const { return sizeof(Assign) / sizeof(qint32); }
++ };
++
++ struct Q_SCXML_EXPORT Initialize: Instruction
++ {
++ EvaluatorId expression;
++
++ static InstructionType kind() { return Instruction::Initialize; }
++ int size() const { return sizeof(Initialize) / sizeof(qint32); }
++ };
++
++ struct Q_SCXML_EXPORT If: Instruction
++ {
++ Array<EvaluatorId> conditions;
++ // InstructionSequences blocks;
++ const InstructionSequences *blocks() const {
++ return reinterpret_cast<const InstructionSequences *>(
++ reinterpret_cast<const InstructionId *>(this) + sizeof(If) / sizeof(qint32)
++ + conditions.dataSize());
++ }
++
++ static InstructionType kind() { return Instruction::If; }
++ int size() const
++ {
++ return sizeof(If) / sizeof(qint32) + blocks()->size() + conditions.dataSize();
++ }
++ };
++
++ struct Q_SCXML_EXPORT Foreach: Instruction
++ {
++ EvaluatorId doIt;
++ InstructionSequence block;
++
++ static InstructionType kind() { return Instruction::Foreach; }
++ int size() const { return sizeof(Foreach) / sizeof(qint32) + block.entryCount; }
++ const InstructionId *blockstart() const
++ {
++ return reinterpret_cast<const InstructionId *>(&block);
++ }
++ };
++
++ struct Q_SCXML_EXPORT Cancel: Instruction
++ {
++ StringId sendid;
++ EvaluatorId sendidexpr;
++
++ static InstructionType kind() { return Instruction::Cancel; }
++ int size() const { return sizeof(Cancel) / sizeof(qint32); }
++ };
++
++ struct StateTable {
++ int version;
++ int name;
++ enum: int {
++ InvalidDataModel = -1,
++ NullDataModel = 0,
++ EcmaScriptDataModel = 1,
++ CppDataModel = 2
++ } dataModel;
++ int childStates; // offset into offsets
++ int initialTransition;
++ int initialSetup;
++ enum: int { InvalidBinding = -1, EarlyBinding = 0, LateBinding = 1 } binding;
++ int maxServiceId;
++ int stateOffset, stateCount;
++ int transitionOffset, transitionCount;
++ int arrayOffset, arraySize;
++
++ enum { terminator = 0xc0ff33 };
++ enum { InvalidIndex = -1 };
++
++ struct State {
++ int name;
++ int parent;
++ enum: int {
++ Invalid = -1,
++ Normal = 0,
++ Parallel = 1,
++ Final = 2,
++ ShallowHistory = 3,
++ DeepHistory = 4
++ } type;
++ int initialTransition;
++ int initInstructions;
++ int entryInstructions;
++ int exitInstructions;
++ int doneData;
++ int childStates; // offset into arrays
++ int transitions; // offset into arrays
++ int serviceFactoryIds; // offset into arrays
++
++ State()
++ : name(InvalidIndex)
++ , parent(InvalidIndex)
++ , type(Invalid)
++ , initialTransition(InvalidIndex)
++ , initInstructions(InvalidIndex)
++ , entryInstructions(InvalidIndex)
++ , exitInstructions(InvalidIndex)
++ , doneData(InvalidIndex)
++ , childStates(InvalidIndex)
++ , transitions(InvalidIndex)
++ , serviceFactoryIds(InvalidIndex)
++ {}
++
++ bool isAtomic() const
++ { return childStates == InvalidIndex; }
++
++ bool isCompound() const
++ { return type == Normal && childStates != InvalidIndex; }
++
++ bool parentIsScxmlElement() const
++ { return parent == InvalidIndex; }
++
++ bool isHistoryState() const
++ { return type == ShallowHistory || type == DeepHistory; }
++
++ bool isParallel() const
++ { return type == Parallel; }
++ };
++
++ struct Transition {
++ int events; // offset into offsets
++ int condition;
++ enum: int {
++ Invalid = -1,
++ Internal = 0,
++ External = 1,
++ Synthetic = 2
++ } type;
++ int source;
++ int targets; // offset into offsets
++ int transitionInstructions;
++
++ Transition()
++ : events(InvalidIndex)
++ , condition(InvalidIndex)
++ , type(Invalid)
++ , source(InvalidIndex)
++ , targets(InvalidIndex)
++ , transitionInstructions(InvalidIndex)
++ {}
++ };
++
++ struct Array {
++ Array(const int *start): start(start) {}
++ int size() const { return *start; }
++ bool isValid() const { return start != nullptr; }
++
++ int operator[](int idx) const {
++ Q_ASSERT(idx >= 0);
++ Q_ASSERT(idx < size());
++ return *(start + idx + 1);
++ }
++
++ struct const_iterator
++ {
++ const_iterator(const Array &a, int pos): a(a), pos(pos) {}
++
++ // std::iterator_traits
++ using value_type = int;
++ using pointer = const int*;
++ using reference = const int&;
++ using difference_type = std::ptrdiff_t;
++ using iterator_category = std::forward_iterator_tag;
++
++ const_iterator &operator++() {
++ if (pos < a.size()) ++pos;
++ return *this;
++ }
++
++ bool operator==(const const_iterator &other) const
++ { return &other.a == &a && other.pos == pos; }
++
++ bool operator!=(const StateTable::Array::const_iterator &other)
++ { return !this->operator==(other); }
++
++ int operator*() const {
++ if (pos < a.size())
++ return a[pos];
++ else
++ return -1;
++ }
++
++ private:
++ const Array &a;
++ int pos;
++ };
++
++ const_iterator begin() const
++ { return const_iterator(*this, 0); }
++
++ const_iterator end() const
++ { return const_iterator(*this, size()); }
++
++ private:
++ const int *start;
++ };
++
++ StateTable()
++ : version(InvalidIndex)
++ , name(InvalidIndex)
++ , dataModel(InvalidDataModel)
++ , childStates(InvalidIndex)
++ , initialTransition(InvalidIndex)
++ , initialSetup(InvalidIndex)
++ , binding(InvalidBinding)
++ , maxServiceId(InvalidIndex)
++ , stateOffset(InvalidIndex), stateCount(InvalidIndex)
++ , transitionOffset(InvalidIndex), transitionCount(InvalidIndex)
++ , arrayOffset(InvalidIndex), arraySize(InvalidIndex)
++ {}
++
++ const State &state(int idx) const
++ {
++ Q_ASSERT(idx >= 0);
++ Q_ASSERT(idx < stateCount);
++ return reinterpret_cast<const State *>(
++ reinterpret_cast<const int *>(this) + stateOffset)[idx];
++ }
++
++ const Transition &transition(int idx) const
++ {
++ Q_ASSERT(idx >= 0);
++ Q_ASSERT(idx < transitionCount);
++ return reinterpret_cast<const Transition *>(
++ reinterpret_cast<const int *>(this) + transitionOffset)[idx];
++ }
++
++ const Array array(int idx) const
++ {
++ Q_ASSERT(idx < arraySize);
++ if (idx >= 0) {
++ const int *start = reinterpret_cast<const int *>(this) + arrayOffset + idx;
++ Q_ASSERT(*start + idx < arraySize);
++ return Array(start);
++ } else {
++ return Array(nullptr);
++ }
++ }
++ };
++
++ #if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
++ #pragma pack(pop)
++ #endif
++
++ } // QScxmlExecutableContent namespace
++
++ class QScxmlExecutionEngine
++ {
++ Q_DISABLE_COPY(QScxmlExecutionEngine)
++
++ public:
++ QScxmlExecutionEngine(QScxmlStateMachine *stateMachine);
++
++ bool execute(QScxmlExecutableContent::ContainerId ip, const QVariant &extraData = QVariant());
++
++ private:
++ const QScxmlExecutableContent::InstructionId *step(
++ const QScxmlExecutableContent::InstructionId *ip, bool *ok);
++
++ QScxmlStateMachine *stateMachine;
++ QVariant extraData;
++ };
++
++ QT_END_NAMESPACE
++
++ #endif // QSCXMLEXECUTABLECONTENT_P_H