Allow moc to handle symbols that have been redefined.

Allow moc to produce the desired identifiers when used with C++
symbol names that have been redefined, for example by -Dfoo=bar.

Two changes are required: firstly, when encoding a type name, the
components of the name must be checked for substitutions that have been
defined for that token (note that this is not done here by correct
pre-processing, but only by processing the resultant table of
definitions).  Secondly, the arguments to the SIGNAL, SLOT and METHOD
macros must be allowed to be substituted during macro expansion rather
than stringized directly.

This is a temporary change to prevent breaking existing projects
that depend on the declarative module.  After clients have had an
opportunity to update their code to the use the new interfaces,
it can be removed.

Task-number: QTBUG-23737
Change-Id: I39e6844cebf6ca7984af6028160b8a3797ac44a5
Reviewed-by: Martin Jones <martin.jones@nokia.com>
This commit is contained in:
Matthew Vogt 2012-02-23 11:12:58 +10:00 committed by Qt by Nokia
parent 4550f0db21
commit 5bb1408927
5 changed files with 278 additions and 11 deletions

View File

@ -208,16 +208,16 @@ Q_CORE_EXPORT const char *qFlagLocation(const char *method);
#ifndef QT_NO_DEBUG
# define QLOCATION "\0" __FILE__ ":" QTOSTRING(__LINE__)
# ifndef QT_NO_KEYWORDS
# define METHOD(a) qFlagLocation("0"#a QLOCATION)
# define METHOD(a) qFlagLocation("0" QTOSTRING(a) QLOCATION)
# endif
# define SLOT(a) qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
# define SLOT(a) qFlagLocation("1" QTOSTRING(a) QLOCATION)
# define SIGNAL(a) qFlagLocation("2" QTOSTRING(a) QLOCATION)
#else
# ifndef QT_NO_KEYWORDS
# define METHOD(a) "0"#a
# define METHOD(a) "0" QTOSTRING(a)
# endif
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
# define SLOT(a) "1" QTOSTRING(a)
# define SIGNAL(a) "2" QTOSTRING(a)
#endif
#define QMETHOD_CODE 0 // member type codes

View File

@ -159,7 +159,7 @@ int runMoc(int _argc, char **_argv)
bool autoInclude = true;
bool defaultInclude = true;
Preprocessor pp;
Moc moc;
Moc moc(pp);
pp.macros["Q_MOC_RUN"];
pp.macros["__cplusplus"];
QByteArray filename;

View File

@ -304,7 +304,9 @@ void Moc::parseFunctionArguments(FunctionDef *def)
arg.rightType += lexem();
}
arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType));
arg.normalizedType = getTypeSubstitution(arg.normalizedType);
arg.typeNameForCast = normalizeType(QByteArray(noRef(arg.type.name) + "(*)" + arg.rightType));
arg.typeNameForCast = getTypeSubstitution(arg.typeNameForCast);
if (test(EQ))
arg.isDefault = true;
def->arguments += arg;
@ -414,6 +416,7 @@ bool Moc::parseFunction(FunctionDef *def, bool inMacro)
}
def->normalizedType = normalizeType(def->type.name);
def->normalizedType = getTypeSubstitution(def->normalizedType);
if (!test(RPAREN)) {
parseFunctionArguments(def);
@ -512,6 +515,7 @@ bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
}
def->normalizedType = normalizeType(def->type.name);
def->normalizedType = getTypeSubstitution(def->normalizedType);
if (!test(RPAREN)) {
parseFunctionArguments(def);
@ -968,6 +972,7 @@ void Moc::createPropertyDef(PropertyDef &propDef)
QVariant.
*/
type = normalizeType(type);
type = getTypeSubstitution(type);
if (type == "QMap")
type = "QMap<QString,QVariant>";
else if (type == "QValueList")
@ -1081,7 +1086,6 @@ void Moc::parseProperty(ClassDef *def)
createPropertyDef(propDef);
next(RPAREN);
if(!propDef.notify.isEmpty())
def->notifyableProperties++;
if (propDef.revision > 0)
@ -1244,7 +1248,8 @@ void Moc::parseInterfaces(ClassDef *def)
}
// resolve from classnames to interface ids
for (int i = 0; i < iface.count(); ++i) {
const QByteArray iid = interface2IdMap.value(iface.at(i).className);
QByteArray className = getTypeSubstitution(iface.at(i).className);
QByteArray iid = interface2IdMap.value(className);
if (iid.isEmpty())
error("Undefined interface");
@ -1502,6 +1507,107 @@ void Moc::checkProperties(ClassDef *cdef)
}
}
QByteArray Moc::getSubstitution(const QByteArray &token) const
{
Macros::ConstIterator it = preprocessor.macros.find(token);
if (it != preprocessor.macros.end() && it->symbols.count() == 1) {
// We can only handle substitutions that result in a single symbol
return it->symbols.at(0).lexem();
}
return QByteArray();
}
QByteArray Moc::getTokenSubstitution(const QByteArray &token) const
{
QByteArray result = token;
QSet<QByteArray> used;
// Process substitution chain until no replacement exists
QByteArray substitution = getSubstitution(result);
while (!substitution.isEmpty()) {
used.insert(result);
result = substitution;
if (used.contains(result)) {
break;
}
substitution = getSubstitution(result);
}
return result;
}
QByteArray Moc::getWordSubstitution(const QByteArray &word) const
{
QByteArray result;
// A word can contain multiple components separated by '*'
int startIndex = 0;
do {
int index = word.indexOf('*', startIndex);
if (index == -1) {
result.append(getTokenSubstitution(word.mid(startIndex)));
} else {
result.append(getTokenSubstitution(word.mid(startIndex, (index - startIndex))));
result.append('*');
}
startIndex = index + 1;
} while (startIndex != 0);
return result;
}
QByteArray Moc::getNameSubstitution(const QByteArray &name) const
{
QByteArray result;
// Parse multiple tokens in this name independently
int startIndex = 0;
do {
int index = name.indexOf(' ', startIndex);
if (index == -1) {
result.append(getWordSubstitution(name.mid(startIndex)));
} else {
result.append(getWordSubstitution(name.mid(startIndex, (index - startIndex))));
result.append(' ');
}
startIndex = index + 1;
} while (startIndex != 0);
return result;
}
QByteArray Moc::getTypeSubstitution(const QByteArray &typeName) const
{
int index = typeName.indexOf('<');
if (index != -1) {
QByteArray templateName = typeName.left(index);
int lastIndex = typeName.lastIndexOf('>');
if (lastIndex > index) {
QByteArray result = getNameSubstitution(templateName);
// Parse the interior type independently
QByteArray parameter = typeName.mid(index + 1, (lastIndex - index - 1));
QByteArray interior = getTypeSubstitution(parameter);
if (interior.endsWith('>')) {
interior.append(' ');
}
result.append('<').append(interior).append(typeName.mid(lastIndex));
return result;
} else {
// Something is broken; return the input unmodified
return typeName;
}
}
return getNameSubstitution(typeName);
}
QT_END_NAMESPACE

View File

@ -43,6 +43,7 @@
#define MOC_H
#include "parser.h"
#include "preprocessor.h"
#include <QStringList>
#include <QMap>
#include <QPair>
@ -197,12 +198,14 @@ struct NamespaceDef {
class Moc : public Parser
{
public:
Moc()
: noInclude(false), generatedCode(false), mustIncludeQMetaTypeH(false), mustIncludeQPluginH(false)
Moc(Preprocessor &p)
: preprocessor(p), noInclude(false), generatedCode(false),
mustIncludeQMetaTypeH(false), mustIncludeQPluginH(false)
{}
QByteArray filename;
Preprocessor &preprocessor;
bool noInclude;
bool generatedCode;
bool mustIncludeQMetaTypeH;
@ -260,6 +263,12 @@ public:
void checkSuperClasses(ClassDef *def);
void checkProperties(ClassDef* cdef);
QByteArray getSubstitution(const QByteArray &token) const;
QByteArray getTokenSubstitution(const QByteArray &token) const;
QByteArray getWordSubstitution(const QByteArray &word) const;
QByteArray getNameSubstitution(const QByteArray &name) const;
QByteArray getTypeSubstitution(const QByteArray &typeName) const;
};
inline QByteArray noRef(const QByteArray &type)

View File

@ -535,6 +535,7 @@ private slots:
void cxx11Enums_data();
void cxx11Enums();
void returnRefs();
void redefinedNames();
signals:
void sigWithUnsignedArg(unsigned foo);
@ -1762,6 +1763,157 @@ void tst_Moc::returnRefs()
// they used to cause miscompilation of the moc generated file.
}
struct ActualInterfaceName
{
virtual ~ActualInterfaceName() {}
virtual void foo() = 0;
};
QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(ActualInterfaceName, "foo.bar.ActualInterfaceName")
QT_END_NAMESPACE
#define DefinedInterfaceName ActualInterfaceName
#define RedefinedInterfaceName DefinedInterfaceName
struct ActualName {};
#define DefinedName ActualName
#define RedefinedName DefinedName
template<typename T>
struct ActualTemplateName {};
#define DefinedTemplateName ActualTemplateName
#define RedefinedTemplateName DefinedTemplateName
#define ActualName ActualName
class RedefinitionTest : public QObject, public RedefinedInterfaceName
{
Q_OBJECT
Q_INTERFACES(RedefinedInterfaceName)
Q_PROPERTY(ActualName p1 READ getP1)
Q_PROPERTY(DefinedName p2 READ getP2)
Q_PROPERTY(RedefinedName p3 READ getP3)
Q_PROPERTY(DefinedName * p4 READ getP4)
Q_PROPERTY(RedefinedName * p5 READ getP5)
Q_PROPERTY(DefinedName ** p6 READ getP6)
Q_PROPERTY(RedefinedName ** p7 READ getP7)
Q_PROPERTY(DefinedName const ** p8 READ getP8)
Q_PROPERTY(RedefinedName const ** p9 READ getP9)
Q_PROPERTY(DefinedName const * const * p10 READ getP10)
Q_PROPERTY(RedefinedName const * const * p11 READ getP11)
Q_PROPERTY(DefinedTemplateName<DefinedName> p16 READ getP16)
Q_PROPERTY(RedefinedTemplateName<RedefinedName> p17 READ getP17)
Q_PROPERTY(DefinedTemplateName<DefinedName **> p18 READ getP18)
Q_PROPERTY(RedefinedTemplateName<RedefinedName **> p19 READ getP19)
Q_PROPERTY(DefinedTemplateName<DefinedName const * const> p20 READ getP20)
Q_PROPERTY(RedefinedTemplateName<RedefinedName const * const> p21 READ getP21)
signals:
void signal1(ActualName);
void signal2(DefinedName);
void signal3(RedefinedName);
public slots:
void slot1(ActualName x) { v = x; }
void slot2(DefinedName x) { v = x; }
void slot3(RedefinedName x) { v = x; }
public:
void foo() {}
ActualName v;
ActualName *vp;
ActualName const *vcp;
ActualTemplateName<ActualName> tv;
ActualTemplateName<ActualName **> tvpp;
ActualTemplateName<ActualName const * const> tvcpc;
ActualName getP0() { return v; }
ActualName getP1() { return v; }
DefinedName getP2() { return v; }
RedefinedName getP3() { return v; }
DefinedName * getP4() { return &v; }
RedefinedName * getP5() { return &v; }
DefinedName ** getP6() { return &vp; }
RedefinedName ** getP7() { return &vp; }
DefinedName const ** getP8() { return &vcp; }
RedefinedName const ** getP9() { return &vcp; }
DefinedName const * const * getP10() const { return &vcp; }
RedefinedName const * const * getP11() const { return &vcp; }
DefinedTemplateName<DefinedName> getP16() { return tv; }
RedefinedTemplateName<RedefinedName> getP17() { return tv; }
DefinedTemplateName<DefinedName **> getP18() { return tvpp; }
RedefinedTemplateName<RedefinedName **> getP19() { return tvpp; }
DefinedTemplateName<DefinedName const * const> getP20() { return tvcpc; }
RedefinedTemplateName<RedefinedName const * const> getP21() { return tvcpc; }
};
void tst_Moc::redefinedNames()
{
RedefinitionTest tst;
const QMetaObject *mobj = tst.metaObject();
QVERIFY(mobj->indexOfProperty("p1") != -1);
// Use the true slot name rather than the declared name
QVERIFY(QObject::connect(&tst, SIGNAL(signal1(ActualName)),
&tst, SLOT(slot1(ActualName))));
QVERIFY(QObject::connect(&tst, SIGNAL(signal2(ActualName)),
&tst, SLOT(slot2(ActualName))));
QVERIFY(QObject::connect(&tst, SIGNAL(signal3(ActualName)),
&tst, SLOT(slot3(ActualName))));
// Use the declared slot name rather than the true name
QVERIFY(QObject::connect(&tst, SIGNAL(signal1(ActualName)),
&tst, SLOT(slot2(DefinedName))));
QVERIFY(QObject::connect(&tst, SIGNAL(signal1(ActualName)),
&tst, SLOT(slot3(RedefinedName))));
// Use the declared signal name rather than the true name
QVERIFY(QObject::connect(&tst, SIGNAL(signal2(DefinedName)),
&tst, SLOT(slot1(ActualName))));
QVERIFY(QObject::connect(&tst, SIGNAL(signal3(RedefinedName)),
&tst, SLOT(slot1(ActualName))));
// Use both declared names
QVERIFY(QObject::connect(&tst, SIGNAL(signal2(DefinedName)),
&tst, SLOT(slot2(DefinedName))));
QVERIFY(QObject::connect(&tst, SIGNAL(signal2(DefinedName)),
&tst, SLOT(slot3(RedefinedName))));
QVERIFY(QObject::connect(&tst, SIGNAL(signal3(RedefinedName)),
&tst, SLOT(slot2(DefinedName))));
QVERIFY(QObject::connect(&tst, SIGNAL(signal3(RedefinedName)),
&tst, SLOT(slot3(RedefinedName))));
}
QTEST_APPLESS_MAIN(tst_Moc)
#include "tst_moc.moc"