diff options
author | Erik Verbruggen <erik.verbruggen@theqtcompany.com> | 2016-03-31 14:48:28 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2016-04-13 12:29:54 +0000 |
commit | 181928775bb5456c88e0528c639274bc7da259c2 (patch) | |
tree | 74c3995c2cd791e0745c182a51f74e9a9ae0a365 /tools | |
parent | 9968c72f7cac511235ab6aab2e948d29da4ff059 (diff) |
Validate IDs and event names, and handle them well in C++ generation.
Change-Id: I2e4e15496e7b2adc2f452745b1341f8fa0140b12
Task-number: QTBUG-51818
Reviewed-by: Jarek Kobus <jaroslaw.kobus@theqtcompany.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qscxmlc/qscxmlc.cpp | 2 | ||||
-rw-r--r-- | tools/qscxmlc/scxmlcppdumper.cpp | 224 | ||||
-rw-r--r-- | tools/qscxmlc/scxmlcppdumper.h | 2 |
3 files changed, 154 insertions, 74 deletions
diff --git a/tools/qscxmlc/qscxmlc.cpp b/tools/qscxmlc/qscxmlc.cpp index c92db22..d5217d8 100644 --- a/tools/qscxmlc/qscxmlc.cpp +++ b/tools/qscxmlc/qscxmlc.cpp @@ -223,7 +223,7 @@ int run(const QStringList &arguments) mainClassName = mainDoc->root->name; if (mainClassName.isEmpty()) { mainClassName = QFileInfo(scxmlFileName).fileName(); - int dot = mainClassName.indexOf(QLatin1Char('.')); + int dot = mainClassName.lastIndexOf(QLatin1Char('.')); if (dot != -1) mainClassName = mainClassName.left(dot); } diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp index 434f0a8..a522c58 100644 --- a/tools/qscxmlc/scxmlcppdumper.cpp +++ b/tools/qscxmlc/scxmlcppdumper.cpp @@ -218,7 +218,7 @@ public: { Q_ASSERT(doc); - clazz.className = translationUnit->classnameForDocument.value(doc); + clazz.className = mangleIdentifier(translationUnit->classnameForDocument.value(doc)); m_qtMode = doc->qtMode; doc->root->accept(this); @@ -274,7 +274,8 @@ protected: clazz.init.impl << QStringLiteral("stateMachine.setTableData(this);"); foreach (AbstractState *s, node->initialStates) { - clazz.init.impl << QStringLiteral("state_%1.setAsInitialStateFor(&stateMachine);").arg(mangledName(s)); + clazz.init.impl << QStringLiteral("%1.setAsInitialStateFor(&stateMachine);") + .arg(mangledName(s, StateName)); } // visit the kids: @@ -326,12 +327,13 @@ protected: bool visit(State *node) Q_DECL_OVERRIDE { - QString name = mangledName(node); - QString stateName = QStringLiteral("state_") + name; + QString name = mangledName(node, PlainName); + QString stateName = mangledName(node, StateName); // Property stuff: if (isValidQPropertyName(node->id)) { - clazz.properties << QStringLiteral("Q_PROPERTY(bool %1 READ %1 NOTIFY %1Changed)") - .arg(node->id); + clazz.properties << QStringLiteral("Q_PROPERTY(bool %1 READ %2 NOTIFY %3)") + .arg(node->id).arg(name) + .arg(mangledName(node, SignalName)); } if (m_qtMode) { Method getter(QStringLiteral("bool %1() const").arg(name)); @@ -355,8 +357,9 @@ protected: clazz.init.impl << stateName + QStringLiteral(".setObjectName(string(%1));").arg(addString(node->id)); } foreach (AbstractState *initialState, node->initialStates) { - clazz.init.impl << stateName + QStringLiteral(".setInitialState(&state_") - + mangledName(initialState) + QStringLiteral(");"); + clazz.init.impl << stateName + QStringLiteral(".setInitialState(&") + + mangledName(initialState, StateName) + + QStringLiteral(");"); } if (node->type == State::Parallel) { clazz.init.impl << stateName + QStringLiteral(".setChildMode(QState::ParallelStates);"); @@ -365,8 +368,8 @@ protected: clazz.init.impl << QStringLiteral("QObject::connect(&") + stateName + QStringLiteral(", SIGNAL(activeChanged(bool)), &stateMachine, SIGNAL(") - + mangledName(node) - + QStringLiteral("Changed(bool)));"); + + mangledName(node, SignalName) + + QStringLiteral("(bool)));"); } m_stateNames.append(node->id); @@ -497,7 +500,7 @@ protected: } QStringList targetNames; foreach (DocumentModel::AbstractState *s, node->targetStates) - targetNames.append(QStringLiteral("&state_") + mangledName(s)); + targetNames.append(QStringLiteral("&") + mangledName(s, StateName)); QString targets = tName + QStringLiteral(".setTargetStates(") + createList(QStringLiteral("QAbstractState*"), targetNames); clazz.init.impl << targets + QStringLiteral(");"); @@ -519,7 +522,7 @@ protected: // Includes: clazz.implIncludes << "QHistoryState"; - QString stateName = QStringLiteral("state_") + mangledName(node); + const QString stateName = mangledName(node, StateName); // Declaration: clazz.classFields << QStringLiteral("QHistoryState ") + stateName + QLatin1Char(';'); @@ -582,11 +585,31 @@ protected: } private: - QString mangledName(AbstractState *state) + enum NameForm { + PlainName, + SignalName, + MachineName, + StateName + }; + + QString mangledName(const QString &id, NameForm form) const + { + QString name = id; + switch (form) { + case PlainName: break; + case SignalName: name.append(QStringLiteral("Changed")); break; + case StateName: name.prepend(QStringLiteral("state_")); break; + case MachineName: name.prepend(QStringLiteral("machine_")); break; + } + + return name.isEmpty() ? name : mangleIdentifier(name); + } + + QString mangledName(AbstractState *state, NameForm form) const { Q_ASSERT(state); - QString name = m_mangledNames.value(state); + QString name = m_mangledNames.value(state)[form]; if (!name.isEmpty()) return name; @@ -597,21 +620,21 @@ private: } } - name = CppDumper::mangleId(id); - m_mangledNames.insert(state, name); + name = mangledName(id, form); + m_mangledNames[state][form] = name; return name; } - QString transitionName(Transition *t) + QString transitionName(Transition *t) const { int idx = 0; QString parentName; auto parent = m_parents.last(); if (State *parentState = parent->asState()) { - parentName = mangledName(parentState); + parentName = mangledName(parentState, PlainName); idx = childIndex(t, parentState->children); } else if (HistoryState *historyState = parent->asHistoryState()) { - parentName = mangledName(historyState); + parentName = mangledName(historyState, PlainName); } else if (Scxml *scxml = parent->asScxml()) { parentName = QStringLiteral("stateMachine"); idx = childIndex(t, scxml->children); @@ -656,11 +679,11 @@ private: return result; } - QString generateInitializer(AbstractState *node) + QString generateInitializer(AbstractState *node) const { - QString init = QStringLiteral("state_") + mangledName(node) + QStringLiteral("("); + QString init = mangledName(node, StateName) + QStringLiteral("("); if (State *parentState = node->parent->asState()) { - init += QStringLiteral("&state_") + mangledName(parentState); + init += QStringLiteral("&") + mangledName(parentState, StateName); } else { init += QStringLiteral("&stateMachine"); } @@ -674,27 +697,31 @@ private: QString name = subDocs->root->name; if (name.isEmpty()) continue; - auto mangledName = CppDumper::mangleId(name); - auto qualifiedName = namespacePrefix + mangledName; - if (m_serviceProps.contains(qMakePair(mangledName, qualifiedName))) + auto plainName = mangledName(name, PlainName); + auto qualifiedName = namespacePrefix + plainName; + if (m_serviceProps.contains(qMakePair(plainName, qualifiedName))) continue; - m_serviceProps.append(qMakePair(mangledName, qualifiedName)); - clazz.classFields << QStringLiteral("%1 *%2;").arg(qualifiedName, mangledName); - clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(mangledName); + m_serviceProps.append(qMakePair(name, qualifiedName)); + clazz.classFields << QStringLiteral("%1 *%2;").arg(qualifiedName, plainName); + clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(plainName); if (isValidQPropertyName(name)) { - clazz.properties << QStringLiteral("Q_PROPERTY(%1%2 *%2 READ %2 NOTIFY %2Changed)") - .arg(namespacePrefix, name); + clazz.properties << QStringLiteral("Q_PROPERTY(%1%2 *%3 READ %2 NOTIFY %4)") + .arg(namespacePrefix, plainName, name, + mangledName(name, SignalName)); } if (m_qtMode) { - Method getter(QStringLiteral("%1 *%2() const").arg(qualifiedName, mangledName)); - getter.impl << QStringLiteral("%1 *%2::%3() const").arg(qualifiedName, clazz.className, mangledName) - << QStringLiteral("{ return data->%1; }").arg(mangledName); + Method getter(QStringLiteral("%1 *%2() const").arg(qualifiedName, plainName)); + getter.impl << QStringLiteral("%1 *%2::%3() const").arg(qualifiedName, + clazz.className, plainName) + << QStringLiteral("{ return data->%1; }").arg(plainName); clazz.publicMethods << getter; - clazz.signalMethods << QStringLiteral("void %1Changed(%2 *statemachine);").arg(mangledName, qualifiedName); + clazz.signalMethods << QStringLiteral("void %1(%2 *statemachine);") + .arg(mangledName(name, SignalName), qualifiedName); } - clazz.dataMethods << QStringLiteral("%1 *machine_%2() const").arg(qualifiedName, mangledName) - << QStringLiteral("{ return %1; }").arg(name) + clazz.dataMethods << QStringLiteral("%1 *%2() const") + .arg(qualifiedName, mangledName(name, MachineName)) + << QStringLiteral("{ return %1; }").arg(plainName) << QString(); } } @@ -731,7 +758,7 @@ private: << QStringLiteral(" %1 *m = static_cast<%1 *>(stateMachine);").arg(clazz.className); foreach (const QString &signalName, m_signalNames) { dm << QStringLiteral(" if (event->name() == %1) { emit m->%2(event->data()); return false; }") - .arg(qba(signalName), CppDumper::mangleId(signalName)); + .arg(qba(signalName), mangleIdentifier(signalName)); } } dm << QStringLiteral(" return true;") @@ -771,13 +798,13 @@ private: return QString(); } - QString parentStateMemberName() + QString parentStateMemberName() const { Node *parent = m_parents.last(); if (State *s = parent->asState()) - return QStringLiteral("state_") + mangledName(s); + return mangledName(s, StateName); else if (HistoryState *h = parent->asHistoryState()) - return QStringLiteral("state_") + mangledName(h); + return mangledName(h, StateName); else if (parent->asScxml()) return QStringLiteral("stateMachine"); else @@ -1090,19 +1117,21 @@ private: if (stateName.isEmpty()) continue; - QByteArray mangledStateName = CppDumper::mangleId(stateName).toUtf8(); + const QByteArray mangledStateName = mangledName(stateName, StateName).toUtf8(); + const QString mangledSignalName = mangledName(stateName, SignalName); FunctionDef signal; signal.type.name = "void"; signal.type.rawName = signal.type.name; signal.normalizedType = signal.type.name; - signal.name = mangledStateName + "Changed"; + signal.name = mangledSignalName.toUtf8(); signal.access = FunctionDef::Private; signal.isSignal = true; if (!m_qtMode) { signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);"; } else { - clazz.signalMethods << QStringLiteral("void %1Changed(bool active);").arg(stateName); + clazz.signalMethods << QStringLiteral("void %1(bool active);") + .arg(mangledSignalName); } ArgumentDef arg; arg.type.name = "bool"; @@ -1117,8 +1146,8 @@ private: PropertyDef prop; prop.name = stateName.toUtf8(); prop.type = "bool"; - prop.read = "data->state_" + mangledStateName + ".active"; - prop.notify = mangledStateName + "Changed"; + prop.read = "data->" + mangledStateName + ".active"; + prop.notify = mangledSignalName.toUtf8(); prop.notifyId = classDef.signalList.size() - 1; prop.gspec = PropertyDef::ValueSpec; prop.scriptable = "true"; @@ -1159,21 +1188,23 @@ private: } for (const auto &service : m_serviceProps) { auto serviceName = service.first; - QString fqServiceClass = service.second; - QByteArray serviceClass = fqServiceClass.toUtf8(); + const QString mangledServiceName = mangledName(serviceName, PlainName); + const QString fqServiceClass = service.second; + const QByteArray serviceClass = fqServiceClass.toUtf8(); knownQObjectClasses.insert(serviceClass, ""); reg.impl << QStringLiteral(" SET_SERVICE_PROP(%1, %2, %3%2, %4)") - .arg(addString(serviceName)) - .arg(serviceName, namespacePrefix).arg(classDef.signalList.size()); + .arg(addString(mangledServiceName)) + .arg(mangledServiceName, namespacePrefix).arg(classDef.signalList.size()); - QByteArray mangledServiceName = CppDumper::mangleId(serviceName).toUtf8(); + const QByteArray mangledMachineName = mangledName(serviceName, MachineName).toUtf8(); + const QByteArray mangledSignalName = mangledName(serviceName, SignalName).toUtf8(); FunctionDef signal; signal.type.name = "void"; signal.type.rawName = signal.type.name; signal.normalizedType = signal.type.name; - signal.name = mangledServiceName + "Changed"; + signal.name = mangledSignalName; signal.access = FunctionDef::Private; signal.isSignal = true; if (!m_qtMode) { @@ -1191,10 +1222,10 @@ private: ++classDef.notifyableProperties; PropertyDef prop; - prop.name = serviceName.toUtf8(); + prop.name = mangledServiceName.toUtf8(); prop.type = serviceClass + "*"; - prop.read = "data->machine_" + mangledServiceName; - prop.notify = mangledServiceName + "Changed"; + prop.read = "data->" + mangledMachineName; + prop.notify = mangledSignalName; prop.notifyId = classDef.signalList.size() - 1; prop.gspec = PropertyDef::ValueSpec; prop.scriptable = "true"; @@ -1215,18 +1246,83 @@ private: return QStringLiteral("string(%1)").arg(addString(bytes)); } - QString scxmlClassName(DocumentModel::ScxmlDocument *doc) const + QString scxmlClassName(DocumentModel::ScxmlDocument *doc) { - QString name = translationUnit->classnameForDocument.value(doc); + QString name = mangleIdentifier(translationUnit->classnameForDocument.value(doc)); Q_ASSERT(!name.isEmpty()); return namespacePrefix + name; } + /*! + * \internal + * Mangles \a str to be a unique C++ identifier. Characters that are invalid for C++ identifiers + * are replaced by the pattern \c _0x<hex>_ where <hex> 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 mangleIdentifier(const QString &str) const + { + 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; + } + private: QString namespacePrefix; ClassDump &clazz; TranslationUnit *translationUnit; - QHash<AbstractState *, QString> m_mangledNames; + mutable QHash<AbstractState *, QHash<NameForm, QString> > m_mangledNames; + mutable QHash<QString, QString> m_mangledToOriginal; QVector<Node *> m_parents; QList<QPair<QString, QString>> m_serviceProps; QSet<QString> m_knownEvents; @@ -1278,20 +1374,6 @@ void CppDumper::dump(TranslationUnit *unit) writeImplEnd(); } -QString CppDumper::mangleId(const QString &id) // TODO: remove -{ - QString mangled(id); - mangled = mangled.replace(QLatin1Char('_'), QLatin1String("__")); - mangled = mangled.replace(QLatin1Char(':'), QLatin1String("_colon_")); - mangled = mangled.replace(QLatin1Char('-'), QLatin1String("_dash_")); - mangled = mangled.replace(QLatin1Char('@'), QLatin1String("_at_")); - mangled = mangled.replace(QLatin1Char('.'), QLatin1String("_dot_")); - mangled = mangled.replace(QLatin1Char(','), QLatin1String("_comma_")); - mangled = mangled.replace(QLatin1Char('('), QLatin1String("_lparen_")); - mangled = mangled.replace(QLatin1Char(')'), QLatin1String("_rparen_")); - return mangled; -} - 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)) diff --git a/tools/qscxmlc/scxmlcppdumper.h b/tools/qscxmlc/scxmlcppdumper.h index be590c0..f02bf2f 100644 --- a/tools/qscxmlc/scxmlcppdumper.h +++ b/tools/qscxmlc/scxmlcppdumper.h @@ -72,8 +72,6 @@ public: void dump(TranslationUnit *unit); - static QString mangleId(const QString &id); - private: void writeHeaderStart(const QString &headerGuard, const QStringList &forwardDecls); void writeClass(const ClassDump &clazz); |