/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtScxml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "scxmlcppdumper.h" #include "generator.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE using namespace QScxmlInternal; namespace { static const QString doNotEditComment = QString::fromLatin1( "//\n" "// Statemachine code from reading SCXML file '%1'\n" "// Created by: The Qt SCXML Compiler version %2 (Qt %3)\n" "// WARNING! All changes made in this file will be lost!\n" "//\n" ); static const QString revisionCheck = QString::fromLatin1( "#if !defined(Q_QSCXMLC_OUTPUT_REVISION)\n" "#error \"The header file '%1' doesn't include .\"\n" "#elif Q_QSCXMLC_OUTPUT_REVISION != %2\n" "#error \"This file was generated using the qscxmlc from %3. It\"\n" "#error \"cannot be used with the include files from this version of Qt.\"\n" "#error \"(The qscxmlc has changed too much.)\"\n" "#endif\n" ); QString cEscape(const QString &str) { QString res; int lastI = 0; for (int i = 0; i < str.length(); ++i) { QChar c = str.at(i); if (c < QLatin1Char(' ') || c == QLatin1Char('\\') || c == QLatin1Char('\"')) { res.append(str.mid(lastI, i - lastI)); lastI = i + 1; if (c == QLatin1Char('\\')) { res.append(QLatin1String("\\\\")); } else if (c == QLatin1Char('\"')) { res.append(QLatin1String("\\\"")); } else if (c == QLatin1Char('\n')) { res.append(QLatin1String("\\n")); } else if (c == QLatin1Char('\r')) { res.append(QLatin1String("\\r")); } else { char buf[6]; ushort cc = c.unicode(); buf[0] = '\\'; buf[1] = 'u'; for (int i = 0; i < 4; ++i) { ushort ccc = cc & 0xF; buf[5 - i] = ccc <= 9 ? '0' + ccc : 'a' + ccc - 10; cc >>= 4; } res.append(QLatin1String(&buf[0], 6)); } } } if (lastI != 0) { res.append(str.mid(lastI)); return res; } return str; } typedef QHash Replacements; static void genTemplate(QTextStream &out, const QString &filename, const Replacements &replacements) { QResource file(filename); if (!file.isValid()) { qFatal("Unable to open template '%s'", qPrintable(filename)); } Q_ASSERT(file.compressionAlgorithm() == QResource::NoCompression); QByteArray data; data = QByteArray::fromRawData(reinterpret_cast(file.data()), int(file.size())); const QString t = QString::fromLatin1(data); data.clear(); int start = 0; for (int openIdx = t.indexOf(QStringLiteral("${"), start); openIdx >= 0; openIdx = t.indexOf(QStringLiteral("${"), start)) { out << t.midRef(start, openIdx - start); openIdx += 2; const int closeIdx = t.indexOf(QLatin1Char('}'), openIdx); Q_ASSERT(closeIdx >= openIdx); QString key = t.mid(openIdx, closeIdx - openIdx); if (!replacements.contains(key)) { qFatal("Replacing '%s' failed: no replacement found", qPrintable(key)); } out << replacements.value(key); start = closeIdx + 1; } out << t.midRef(start); } static const char *headerStart = "#include \n" "#include \n" "#include \n" "\n"; using namespace DocumentModel; QString createContainer(const QStringList &elements) { QString result; if (elements.isEmpty()) { result += QStringLiteral("{}"); } else { result += QStringLiteral("{ ") + elements.join(QStringLiteral(", ")) + QStringLiteral(" }"); } return result; } static void generateList(QString &out, std::function next) { const int maxLineLength = 80; QString line; for (int i = 0; ; ++i) { const QString nr = next(i); if (nr.isNull()) break; if (i != 0) line += QLatin1Char(','); if (line.length() + nr.length() + 1 > maxLineLength) { out += line + QLatin1Char('\n'); line.clear(); } else if (i != 0) { line += QLatin1Char(' '); } line += nr; } if (!line.isEmpty()) out += line; } void generateTables(const GeneratedTableData &td, Replacements &replacements) { { // instructions auto instr = td.theInstructions; QString out; generateList(out, [&instr](int idx) -> QString { if (instr.isEmpty() && idx == 0) // prevent generation of empty array return QStringLiteral("-1"); if (idx < instr.size()) return QString::number(instr.at(idx)); else return QString(); }); replacements[QStringLiteral("theInstructions")] = out; } { // dataIds auto dataIds = td.theDataNameIds; QString out; generateList(out, [&dataIds](int idx) -> QString { if (dataIds.size() == 0 && idx == 0) // prevent generation of empty array return QStringLiteral("-1"); if (idx < dataIds.size()) return QString::number(dataIds[idx]); else return QString(); }); replacements[QStringLiteral("dataNameCount")] = QString::number(dataIds.size()); replacements[QStringLiteral("dataIds")] = out; } { // evaluators auto evaluators = td.theEvaluators; QString out; generateList(out, [&evaluators](int idx) -> QString { if (evaluators.isEmpty() && idx == 0) // prevent generation of empty array return QStringLiteral("{ -1, -1 }"); if (idx >= evaluators.size()) return QString(); const auto eval = evaluators.at(idx); return QStringLiteral("{ %1, %2 }").arg(eval.expr).arg(eval.context); }); replacements[QStringLiteral("evaluatorCount")] = QString::number(evaluators.size()); replacements[QStringLiteral("evaluators")] = out; } { // assignments auto assignments = td.theAssignments; QString out; generateList(out, [&assignments](int idx) -> QString { if (assignments.isEmpty() && idx == 0) // prevent generation of empty array return QStringLiteral("{ -1, -1, -1 }"); if (idx >= assignments.size()) return QString(); auto assignment = assignments.at(idx); return QStringLiteral("{ %1, %2, %3 }") .arg(assignment.dest).arg(assignment.expr).arg(assignment.context); }); replacements[QStringLiteral("assignmentCount")] = QString::number(assignments.size()); replacements[QStringLiteral("assignments")] = out; } { // foreaches auto foreaches = td.theForeaches; QString out; generateList(out, [&foreaches](int idx) -> QString { if (foreaches.isEmpty() && idx == 0) // prevent generation of empty array return QStringLiteral("{ -1, -1, -1, -1 }"); if (idx >= foreaches.size()) return QString(); auto foreachItem = foreaches.at(idx); return QStringLiteral("{ %1, %2, %3, %4 }").arg(foreachItem.array).arg(foreachItem.item) .arg(foreachItem.index).arg(foreachItem.context); }); replacements[QStringLiteral("foreachCount")] = QString::number(foreaches.size()); replacements[QStringLiteral("foreaches")] = out; } { // strings QString out; auto strings = td.theStrings; if (strings.isEmpty()) // prevent generation of empty array strings.append(QStringLiteral("")); int ucharCount = 0; generateList(out, [&ucharCount, &strings](int idx) -> QString { if (idx >= strings.size()) return QString(); const int length = strings.at(idx).size(); const QString str = QStringLiteral("STR_LIT(%1, %2, %3)").arg( QString::number(idx), QString::number(ucharCount), QString::number(length)); ucharCount += length + 1; return str; }); replacements[QStringLiteral("stringCount")] = QString::number(strings.size()); replacements[QStringLiteral("strLits")] = out; out.clear(); for (int i = 0, ei = strings.size(); i < ei; ++i) { const QString &string = strings.at(i); QString result; if (i != 0) result += QLatin1Char('\n'); for (int charPos = 0, eCharPos = string.size(); charPos < eCharPos; ++charPos) { result.append(QStringLiteral("0x%1,") .arg(QString::number(string.at(charPos).unicode(), 16))); } result.append(QStringLiteral("0%1 // %2: %3") .arg(QLatin1String(i < ei - 1 ? "," : ""), QString::number(i), cEscape(string))); out += result; } replacements[QStringLiteral("uniLits")] = out; replacements[QStringLiteral("stringdataSize")] = QString::number(ucharCount + 1); } } void generateCppDataModelEvaluators(const GeneratedTableData::DataModelInfo &info, Replacements &replacements) { const QString switchStart = QStringLiteral(" switch (id) {\n"); const QString switchEnd = QStringLiteral(" default: break;\n }"); const QString unusedId = QStringLiteral(" Q_UNUSED(id);"); QString stringEvals; if (!info.stringEvaluators.isEmpty()) { stringEvals += switchStart; for (auto it = info.stringEvaluators.constBegin(), eit = info.stringEvaluators.constEnd(); it != eit; ++it) { stringEvals += QStringLiteral(" case %1:\n").arg(it.key()); stringEvals += QStringLiteral(" return [this]()->QString{ return %1; }();\n") .arg(it.value()); } stringEvals += switchEnd; } else { stringEvals += unusedId; } replacements[QStringLiteral("evaluateToStringCases")] = stringEvals; QString boolEvals; if (!info.boolEvaluators.isEmpty()) { boolEvals += switchStart; for (auto it = info.boolEvaluators.constBegin(), eit = info.boolEvaluators.constEnd(); it != eit; ++it) { boolEvals += QStringLiteral(" case %1:\n").arg(it.key()); boolEvals += QStringLiteral(" return [this]()->bool{ return %1; }();\n") .arg(it.value()); } boolEvals += switchEnd; } else { boolEvals += unusedId; } replacements[QStringLiteral("evaluateToBoolCases")] = boolEvals; QString variantEvals; if (!info.variantEvaluators.isEmpty()) { variantEvals += switchStart; for (auto it = info.variantEvaluators.constBegin(), eit = info.variantEvaluators.constEnd(); it != eit; ++it) { variantEvals += QStringLiteral(" case %1:\n").arg(it.key()); variantEvals += QStringLiteral(" return [this]()->QVariant{ return %1; }();\n") .arg(it.value()); } variantEvals += switchEnd; } else { variantEvals += unusedId; } replacements[QStringLiteral("evaluateToVariantCases")] = variantEvals; QString voidEvals; if (!info.voidEvaluators.isEmpty()) { voidEvals = switchStart; for (auto it = info.voidEvaluators.constBegin(), eit = info.voidEvaluators.constEnd(); it != eit; ++it) { voidEvals += QStringLiteral(" case %1:\n").arg(it.key()); voidEvals += QStringLiteral(" [this]()->void{ %1 }();\n").arg(it.value()); voidEvals += QStringLiteral(" return;\n"); } voidEvals += switchEnd; } else { voidEvals += unusedId; } replacements[QStringLiteral("evaluateToVoidCases")] = voidEvals; } int createFactoryId(QStringList &factories, const QString &className, const QString &namespacePrefix, const QScxmlExecutableContent::InvokeInfo &invokeInfo, const QVector &namelist, const QVector ¶meters) { const int idx = factories.size(); QString line = QStringLiteral("case %1: return new ").arg(QString::number(idx)); if (invokeInfo.expr == QScxmlExecutableContent::NoEvaluator) { line += QStringLiteral("QScxmlStaticScxmlServiceFactory(&%1::%2::staticMetaObject,") .arg(namespacePrefix, className); } else { line += QStringLiteral("QScxmlDynamicScxmlServiceFactory("); } line += QStringLiteral("invoke(%1, %2, %3, %4, %5, %6, %7), ") .arg(QString::number(invokeInfo.id), QString::number(invokeInfo.prefix), QString::number(invokeInfo.expr), QString::number(invokeInfo.location), QString::number(invokeInfo.context), QString::number(invokeInfo.finalize)) .arg(invokeInfo.autoforward ? QStringLiteral("true") : QStringLiteral("false")); { QStringList l; for (auto name : namelist) { l.append(QString::number(name)); } line += QStringLiteral("%1, ").arg(createContainer(l)); } { QStringList l; for (const auto ¶meter : parameters) { l += QStringLiteral("param(%1, %2, %3)") .arg(QString::number(parameter.name), QString::number(parameter.expr), QString::number(parameter.location)); } line += QStringLiteral("%1);").arg(createContainer(l)); } factories.append(line); return idx; } } // anonymous namespace void CppDumper::dump(TranslationUnit *unit) { Q_ASSERT(unit); Q_ASSERT(unit->mainDocument); m_translationUnit = unit; QString namespacePrefix; if (!m_translationUnit->namespaceName.isEmpty()) { namespacePrefix = QStringLiteral("::%1").arg(m_translationUnit->namespaceName); } QStringList classNames; QVector tables; QVector metaDataInfos; QVector dataModelInfos; QVector factories; auto docs = m_translationUnit->allDocuments; tables.resize(docs.size()); metaDataInfos.resize(tables.size()); dataModelInfos.resize(tables.size()); factories.resize(tables.size()); auto classnameForDocument = m_translationUnit->classnameForDocument; for (int i = 0, ei = docs.size(); i != ei; ++i) { auto doc = docs.at(i); auto metaDataInfo = &metaDataInfos[i]; GeneratedTableData::build(doc, &tables[i], metaDataInfo, &dataModelInfos[i], [this, &factories, i, &classnameForDocument, &namespacePrefix]( const QScxmlExecutableContent::InvokeInfo &invokeInfo, const QVector &names, const QVector ¶meters, const QSharedPointer &content) -> int { QString className; if (invokeInfo.expr == QScxmlExecutableContent::NoEvaluator) { className = mangleIdentifier(classnameForDocument.value(content.data())); } return createFactoryId(factories[i], className, namespacePrefix, invokeInfo, names, parameters); }); classNames.append(mangleIdentifier(classnameForDocument.value(doc))); } const QString headerName = QFileInfo(m_translationUnit->outHFileName).fileName(); const QString headerGuard = headerName.toUpper() .replace(QLatin1Char('.'), QLatin1Char('_')) .replace(QLatin1Char('-'), QLatin1Char('_')); const QStringList forwardDecls = classNames.mid(1); writeHeaderStart(headerGuard, forwardDecls); writeImplStart(); for (int i = 0, ei = tables.size(); i != ei; ++i) { const GeneratedTableData &table = tables.at(i); DocumentModel::ScxmlDocument *doc = docs.at(i); writeClass(classNames.at(i), metaDataInfos.at(i)); writeImplBody(table, classNames.at(i), doc, factories.at(i), metaDataInfos.at(i)); if (doc->root->dataModel == DocumentModel::Scxml::CppDataModel) { Replacements r; r[QStringLiteral("datamodel")] = doc->root->cppDataModelClassName; generateCppDataModelEvaluators(dataModelInfos.at(i), r); genTemplate(cpp, QStringLiteral(":/cppdatamodel.t"), r); } } writeHeaderEnd(headerGuard, classNames); writeImplEnd(); } void CppDumper::writeHeaderStart(const QString &headerGuard, const QStringList &forwardDecls) { h << doNotEditComment.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR)) << endl; h << QStringLiteral("#ifndef ") << headerGuard << endl << QStringLiteral("#define ") << headerGuard << endl << endl; h << l(headerStart); if (!m_translationUnit->namespaceName.isEmpty()) h << l("namespace ") << m_translationUnit->namespaceName << l(" {") << endl << endl; if (!forwardDecls.isEmpty()) { for (const QString &forwardDecl : forwardDecls) h << QStringLiteral("class %1;").arg(forwardDecl) << endl; h << endl; } } void CppDumper::writeClass(const QString &className, const GeneratedTableData::MetaDataInfo &info) { Replacements r; r[QStringLiteral("classname")] = className; r[QStringLiteral("properties")] = generatePropertyDecls(info); if (m_translationUnit->stateMethods) { r[QStringLiteral("accessors")] = generateAccessorDecls(info); r[QStringLiteral("signals")] = generateSignalDecls(info); } else { r[QStringLiteral("accessors")] = QString(); r[QStringLiteral("signals")] = QString(); } genTemplate(h, QStringLiteral(":/decl.t"), r); } void CppDumper::writeHeaderEnd(const QString &headerGuard, const QStringList &metatypeDecls) { QString ns; if (!m_translationUnit->namespaceName.isEmpty()) { h << QStringLiteral("} // %1 namespace ").arg(m_translationUnit->namespaceName) << endl << endl; ns = QStringLiteral("::%1").arg(m_translationUnit->namespaceName); } for (const QString &name : metatypeDecls) { h << QStringLiteral("Q_DECLARE_METATYPE(%1::%2*)").arg(ns, name) << endl; } h << endl; h << QStringLiteral("#endif // ") << headerGuard << endl; } void CppDumper::writeImplStart() { cpp << doNotEditComment.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), l(QT_VERSION_STR)) << endl; QStringList includes; for (DocumentModel::ScxmlDocument *doc : qAsConst(m_translationUnit->allDocuments)) { switch (doc->root->dataModel) { case DocumentModel::Scxml::NullDataModel: includes += l("QScxmlNullDataModel"); break; case DocumentModel::Scxml::JSDataModel: includes += l("QScxmlEcmaScriptDataModel"); break; case DocumentModel::Scxml::CppDataModel: includes += doc->root->cppDataModelHeaderName; break; } } includes.sort(); includes.removeDuplicates(); QString headerName = QFileInfo(m_translationUnit->outHFileName).fileName(); cpp << l("#include \"") << headerName << l("\"") << endl; cpp << endl << QStringLiteral("#include ") << endl << QStringLiteral("#include ") << endl; for (const QString &inc : qAsConst(includes)) { cpp << l("#include <") << inc << l(">") << endl; } cpp << endl << revisionCheck.arg(m_translationUnit->scxmlFileName, QString::number(Q_QSCXMLC_OUTPUT_REVISION), QString::fromLatin1(QT_VERSION_STR)) << endl; if (!m_translationUnit->namespaceName.isEmpty()) cpp << l("namespace ") << m_translationUnit->namespaceName << l(" {") << endl << endl; } void CppDumper::writeImplBody(const GeneratedTableData &table, const QString &className, DocumentModel::ScxmlDocument *doc, const QStringList &factory, const GeneratedTableData::MetaDataInfo &info) { QString dataModelField, dataModelInitialization; switch (doc->root->dataModel) { case DocumentModel::Scxml::NullDataModel: dataModelField = l("QScxmlNullDataModel dataModel;"); dataModelInitialization = l("stateMachine.setDataModel(&dataModel);"); break; case DocumentModel::Scxml::JSDataModel: dataModelField = l("QScxmlEcmaScriptDataModel dataModel;"); dataModelInitialization = l("stateMachine.setDataModel(&dataModel);"); break; case DocumentModel::Scxml::CppDataModel: dataModelField = QStringLiteral("// Data model %1 is set from outside.").arg( doc->root->cppDataModelClassName); dataModelInitialization = dataModelField; break; } QString name; if (table.theName == -1) { name = QStringLiteral("QString()"); } else { name = QStringLiteral("string(%1)").arg(table.theName); } QString serviceFactories; if (factory.isEmpty()) { serviceFactories = QStringLiteral(" Q_UNUSED(id);\n Q_UNREACHABLE();"); } else { serviceFactories = QStringLiteral(" switch (id) {\n ") + factory.join(QStringLiteral("\n ")) + QStringLiteral("\n default: Q_UNREACHABLE();\n }"); } Replacements r; r[QStringLiteral("classname")] = className; r[QStringLiteral("name")] = name; r[QStringLiteral("initialSetup")] = QString::number(table.initialSetup()); generateTables(table, r); r[QStringLiteral("dataModelField")] = dataModelField; r[QStringLiteral("dataModelInitialization")] = dataModelInitialization; r[QStringLiteral("theStateMachineTable")] = GeneratedTableData::toString(table.stateMachineTable()); r[QStringLiteral("metaObject")] = generateMetaObject(className, info); r[QStringLiteral("serviceFactories")] = serviceFactories; genTemplate(cpp, QStringLiteral(":/data.t"), r); } void CppDumper::writeImplEnd() { if (!m_translationUnit->namespaceName.isEmpty()) { cpp << endl << QStringLiteral("} // %1 namespace").arg(m_translationUnit->namespaceName) << endl; } } /*! * \internal * Mangles \a str to be a unique C++ identifier. Characters that are invalid for C++ identifiers * are replaced by the pattern \c _0x_ where is the hexadecimal unicode * representation of the character. As identifiers with leading underscores followed by either * another underscore or a capital letter are reserved in C++, we also escape those, by escaping * the first underscore, using the above method. * * We keep track of all identifiers we have used so far and if we find two different names that * map to the same mangled identifier by the above method, we append underscores to the new one * until the result is unique. * * \note * Although C++11 allows for non-ascii (unicode) characters to be used in identifiers, * many compilers forgot to read the spec and do not implement this. Some also do not * implement C99 identifiers, because that is \e {at the implementation's discretion}. So, * we are stuck with plain old boring identifiers. */ QString CppDumper::mangleIdentifier(const QString &str) { auto isNonDigit = [](QChar c) -> bool { return (c >= QLatin1Char('a') && c <= QLatin1Char('z')) || (c >= QLatin1Char('A') && c <= QLatin1Char('Z')) || c == QLatin1Char('_'); }; Q_ASSERT(!str.isEmpty()); QString mangled; mangled.reserve(str.size()); int i = 0; if (str.startsWith(QLatin1Char('_')) && str.size() > 1) { QChar ch = str.at(1); if (ch == QLatin1Char('_') || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) { mangled += QLatin1String("_0x5f_"); ++i; } } for (int ei = str.length(); i != ei; ++i) { auto c = str.at(i).unicode(); if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || isNonDigit(c)) { mangled += c; } else { mangled += QLatin1String("_0x") + QString::number(c, 16) + QLatin1Char('_'); } } while (true) { auto it = m_mangledToOriginal.constFind(mangled); if (it == m_mangledToOriginal.constEnd()) { m_mangledToOriginal.insert(mangled, str); break; } else if (it.value() == str) { break; } mangled += QStringLiteral("_"); // append underscores until we get a unique name } return mangled; } QString CppDumper::generatePropertyDecls(const GeneratedTableData::MetaDataInfo &info) { QString decls; for (const QString &stateName : info.stateNames) { if (stateName.isEmpty()) continue; if (m_translationUnit->stateMethods) { decls += QString::fromLatin1(" Q_PROPERTY(bool %1 READ %2 NOTIFY %3)\n") .arg(stateName, mangleIdentifier(stateName), mangleIdentifier(stateName + QStringLiteral("Changed"))); } else { decls += QString::fromLatin1(" Q_PROPERTY(bool %1)\n").arg(stateName); } } return decls; } QString CppDumper::generateAccessorDecls(const GeneratedTableData::MetaDataInfo &info) { QString decls; for (const QString &stateName : info.stateNames) { if (!stateName.isEmpty()) decls += QString::fromLatin1(" bool %1() const;\n").arg(mangleIdentifier(stateName)); } return decls; } QString CppDumper::generateSignalDecls(const GeneratedTableData::MetaDataInfo &info) { QString decls; for (const QString &stateName : info.stateNames) { if (!stateName.isEmpty()) { decls += QString::fromLatin1(" void %1(bool);\n") .arg(mangleIdentifier(stateName + QStringLiteral("Changed"))); } } return decls; } QString CppDumper::generateMetaObject(const QString &className, const GeneratedTableData::MetaDataInfo &info) { ClassDef classDef; classDef.classname = className.toUtf8(); classDef.qualified = classDef.classname; classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public); classDef.hasQObject = true; FunctionDef constructor; constructor.name = className.toUtf8(); constructor.access = FunctionDef::Public; constructor.isInvokable = true; constructor.isConstructor = true; ArgumentDef arg; arg.type.name = "QObject *"; arg.type.rawName = arg.type.name; arg.normalizedType = arg.type.name; arg.name = "parent"; arg.typeNameForCast = arg.type.name + "*"; constructor.arguments.append(arg); classDef.constructorList.append(constructor); // stateNames: int stateIdx = 0; for (const QString &stateName : info.stateNames) { if (stateName.isEmpty()) continue; QByteArray utf8StateName = stateName.toUtf8(); FunctionDef signal; signal.type.name = "void"; signal.type.rawName = signal.type.name; signal.normalizedType = signal.type.name; signal.name = utf8StateName + "Changed"; if (m_translationUnit->stateMethods) signal.mangledName = mangleIdentifier(stateName + QStringLiteral("Changed")).toUtf8(); signal.access = FunctionDef::Public; signal.isSignal = true; signal.implementation = "QMetaObject::activate(%s, &staticMetaObject, %d, _a);"; ArgumentDef arg; arg.type.name = "bool"; arg.type.rawName = arg.type.name; arg.normalizedType = arg.type.name; arg.name = "active"; arg.typeNameForCast = arg.type.name + "*"; signal.arguments << arg; classDef.signalList << signal; ++classDef.notifyableProperties; PropertyDef prop; prop.name = stateName.toUtf8(); if (m_translationUnit->stateMethods) prop.mangledName = mangleIdentifier(stateName).toUtf8(); prop.type = "bool"; prop.read = "isActive(" + QByteArray::number(stateIdx++) + ")"; prop.notify = utf8StateName + "Changed"; prop.notifyId = classDef.signalList.size() - 1; prop.gspec = PropertyDef::ValueSpec; prop.scriptable = "true"; classDef.propertyList << prop; } // sub-statemachines: QHash knownQObjectClasses; knownQObjectClasses.insert(QByteArray("QScxmlStateMachine"), QByteArray()); QBuffer buf; buf.open(QIODevice::WriteOnly); Generator generator(&classDef, QList(), knownQObjectClasses, QHash(), buf); generator.generateCode(); if (m_translationUnit->stateMethods) { generator.generateAccessorDefs(); generator.generateSignalDefs(); } buf.close(); return QString::fromUtf8(buf.buffer()); } QT_END_NAMESPACE