From aea68c93ae437a761584719f0f1ca93eaf6f7484 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 22 Nov 2012 11:03:39 +0100 Subject: Proper macro replacment and branch evaluation Correclty replace macros according to the C++ standard. Use the correct replacement method also to evaluate With this moc correctly processes boost headers. Task-number: QTBUG-27546 Change-Id: I001b3054c5fcdc34d46cfa53d1387bd19436f361 Reviewed-by: Olivier Goffart --- src/tools/moc/preprocessor.cpp | 161 ++++++++++++++++++++++------------------- src/tools/moc/preprocessor.h | 8 +- src/tools/moc/symbols.h | 55 +++++++++++++- 3 files changed, 144 insertions(+), 80 deletions(-) (limited to 'src/tools/moc') diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp index 49700a0992..72fecf0a46 100644 --- a/src/tools/moc/preprocessor.cpp +++ b/src/tools/moc/preprocessor.cpp @@ -536,50 +536,84 @@ static Symbols tokenize(const QByteArray &input, int lineNum = 1, TokenizeMode m return symbols; } -void Preprocessor::macroExpandSymbols(int lineNum, const Symbols &symbolList, Symbols &expanded, MacroSafeSet safeset) +Symbols Preprocessor::macroExpand(Preprocessor *that, Symbols &toExpand, int &index, int lineNum, bool one) { - Symbols saveSymbols = symbols; - int saveIndex = index; - symbols = symbolList; - index = 0; + SymbolStack symbols; + SafeSymbols sf; + sf.symbols = toExpand; + sf.index = index; + symbols.push(sf); - while (hasNext()) { - next(); - macroExpandIdentifier(lineNum, expanded, safeset); + Symbols result; + if (toExpand.isEmpty()) + return result; + + for (;;) { + QByteArray macro; + Symbols newSyms = macroExpandIdentifier(that, symbols, lineNum, ¯o); + + if (macro.isEmpty()) { + result += newSyms; + } else { + SafeSymbols sf; + sf.symbols = newSyms; + sf.index = 0; + sf.expandedMacro = macro; + symbols.push(sf); + } + if (!symbols.hasNext() || (one && symbols.size() == 1)) + break; + symbols.next(); } - symbols = saveSymbols; - index = saveIndex; + if (symbols.size()) + index = symbols.top().index; + else + index = toExpand.size(); + + return result; } -void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, MacroSafeSet safeset) + +Symbols Preprocessor::macroExpandIdentifier(Preprocessor *that, SymbolStack &symbols, int lineNum, QByteArray *macroName) { - const Symbol &s = symbol(); + Symbol s = symbols.symbol(); + // not a macro - if (s.token != PP_IDENTIFIER || !macros.contains(s) || safeset.contains(s)) { - preprocessed += s; - preprocessed.last().lineNum = lineNum; - return; + if (s.token != PP_IDENTIFIER || !that->macros.contains(s) || symbols.dontReplaceSymbol(s.lexem())) { + Symbols syms; + syms += s; + syms.last().lineNum = lineNum; + return syms; } - const Macro ¯o = macros.value(s); - safeset += s; + const Macro ¯o = that->macros.value(s); + *macroName = s.lexem(); - if (macro.isFunction) { - while (test(PP_WHITESPACE)) {} - if (!test(PP_LPAREN)) { - preprocessed += s; - return; + Symbols expansion; + if (!macro.isFunction) { + expansion = macro.symbols; + } else { + bool haveSpace = false; + while (symbols.test(PP_WHITESPACE)) { haveSpace = true; } + if (!symbols.test(PP_LPAREN)) { + *macroName = QByteArray(); + Symbols syms; + if (haveSpace) + syms += Symbol(lineNum, PP_WHITESPACE); + syms += s; + syms.last().lineNum = lineNum; + return syms; } QList arguments; - while (hasNext()) { + while (symbols.hasNext()) { Symbols argument; // strip leading space - while (test(PP_WHITESPACE)) {} + while (symbols.test(PP_WHITESPACE)) {} int nesting = 0; bool vararg = macro.isVariadic && (arguments.size() == macro.arguments.size() - 1); - while (hasNext()) { - Token t = next(); + while (symbols.hasNext()) { + Token t = symbols.next(); if (t == PP_LPAREN) { ++nesting; } else if (t == PP_RPAREN) { @@ -590,7 +624,7 @@ void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, Mac if (!vararg) break; } - argument += symbol(); + argument += symbols.symbol(); } arguments += argument; @@ -606,11 +640,9 @@ void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, Mac // 0 argument macros are a bit special. They are ok if the // argument is pure whitespace or empty (macro.arguments.size() != 0 || arguments.size() != 1 || !arguments.at(0).isEmpty())) - error("Macro argument mismatch."); + that->error("Macro argument mismatch."); // now replace the macro arguments with the expanded arguments - - Symbols expansion; enum Mode { Normal, Hash, @@ -627,10 +659,10 @@ void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, Mac if (mode == Normal) { if (index >= 0) { // each argument undoergoes macro expansion if it's not used as part of a # or ## - if (i < macro.symbols.size() - 1 && macro.symbols.at(i + 1).token != PP_HASHHASH) { - Symbols expanded; - macroExpandSymbols(lineNum, arguments.at(index), expanded, safeset); - expansion += expanded; + if (i == macro.symbols.size() - 1 || macro.symbols.at(i + 1).token != PP_HASHHASH) { + Symbols arg = arguments.at(index); + int idx = 1; + expansion += macroExpand(that, arg, idx, lineNum, false); } else { expansion += arguments.at(index); } @@ -639,7 +671,7 @@ void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, Mac } } else if (mode == Hash) { if (index < 0) - error("'#' is not followed by a macro parameter"); + that->error("'#' is not followed by a macro parameter"); const Symbols &arg = arguments.at(index); QByteArray stringified; @@ -656,11 +688,6 @@ void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, Mac while (expansion.size() && expansion.last().token == PP_WHITESPACE) expansion.pop_back(); - if (!expansion.size()) - error("'##' can't appear first in macro argument"); - - Symbol last = expansion.last(); - expansion.pop_back(); Symbol next = s; if (index >= 0) { @@ -672,14 +699,16 @@ void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, Mac next = arg.at(0); } - if (last.token == STRING_LITERAL || s.token == STRING_LITERAL) - error("Can't concatenate non identifier tokens"); + if (!expansion.isEmpty() && expansion.last().token == s.token) { + Symbol last = expansion.last(); + expansion.pop_back(); + + if (last.token == STRING_LITERAL || s.token == STRING_LITERAL) + that->error("Can't concatenate non identifier tokens"); - if (last.token == s.token) { QByteArray lexem = last.lexem() + next.lexem(); expansion += Symbol(lineNum, last.token, lexem); } else { - expansion += last; expansion += next; } @@ -692,42 +721,19 @@ void Preprocessor::macroExpandIdentifier(int lineNum, Symbols &preprocessed, Mac mode = Normal; } if (mode != Normal) - error("'#' or '##' found at the end of a macro argument"); + that->error("'#' or '##' found at the end of a macro argument"); - macroExpandSymbols(lineNum, expansion, preprocessed, safeset); - } else { - macroExpandSymbols(lineNum, macro.symbols, preprocessed, safeset); } -} - - -void Preprocessor::substituteMacro(const MacroName ¯o, Symbols &substituted, MacroSafeSet safeset) -{ - Symbols saveSymbols = symbols; - int saveIndex = index; - symbols = macros.value(macro).symbols; - index = 0; - - safeset += macro; - substituteUntilNewline(substituted, safeset); - - symbols = saveSymbols; - index = saveIndex; + return expansion; } - - -void Preprocessor::substituteUntilNewline(Symbols &substituted, MacroSafeSet safeset) +void Preprocessor::substituteUntilNewline(Symbols &substituted) { while (hasNext()) { Token token = next(); if (token == PP_IDENTIFIER) { - MacroName macro = symbol(); - if (macros.contains(macro) && !safeset.contains(macro)) { - substituteMacro(macro, substituted, safeset); - continue; - } + substituted += macroExpand(this, symbols, index, symbol().lineNum, true); } else if (token == PP_DEFINED) { bool braces = test(PP_LPAREN); next(PP_IDENTIFIER); @@ -740,8 +746,9 @@ void Preprocessor::substituteUntilNewline(Symbols &substituted, MacroSafeSet saf } else if (token == PP_NEWLINE) { substituted += symbol(); break; + } else { + substituted += symbol(); } - substituted += symbol(); } } @@ -1092,6 +1099,12 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) (macro.symbols.last().token == PP_WHITESPACE || macro.symbols.last().token == WHITESPACE)) macro.symbols.pop_back(); + if (macro.symbols.first().token == PP_HASHHASH || + macro.symbols.last().token == PP_HASHHASH) + error("'##' cannot appear at either end of a macro expansion"); + if (macro.symbols.last().token == HASH || + macro.symbols.last().token == PP_HASH) + error("'#' is not followed by a macro parameter"); macros.insert(name, macro); continue; } @@ -1104,7 +1117,7 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) } case PP_IDENTIFIER: { // substitute macros - macroExpandIdentifier(symbol().lineNum, preprocessed); + preprocessed += macroExpand(this, symbols, index, symbol().lineNum, true); continue; } case PP_HASH: diff --git a/src/tools/moc/preprocessor.h b/src/tools/moc/preprocessor.h index 0c099318db..df5d28f473 100644 --- a/src/tools/moc/preprocessor.h +++ b/src/tools/moc/preprocessor.h @@ -64,7 +64,6 @@ typedef QByteArray MacroName; typedef SubArray MacroName; #endif typedef QHash Macros; -typedef QVector MacroSafeSet; class QIODevice; @@ -84,10 +83,9 @@ public: void skipUntilEndif(); bool skipBranch(); - void substituteMacro(const MacroName ¯o, Symbols &substituted, MacroSafeSet safeset = MacroSafeSet()); - void substituteUntilNewline(Symbols &substituted, MacroSafeSet safeset = MacroSafeSet()); - void macroExpandIdentifier(int lineNum, Symbols &preprocessed, MacroSafeSet safeset = MacroSafeSet()); - void macroExpandSymbols(int lineNum, const Symbols &symbols, Symbols &expanded, MacroSafeSet safeset); + void substituteUntilNewline(Symbols &substituted); + static Symbols macroExpandIdentifier(Preprocessor *that, SymbolStack &symbols, int lineNum, QByteArray *macroName); + static Symbols macroExpand(Preprocessor *that, Symbols &toExpand, int &index, int lineNum, bool one); int evaluateCondition(); diff --git a/src/tools/moc/symbols.h b/src/tools/moc/symbols.h index c5efbd8cb4..fb2ec03b4b 100644 --- a/src/tools/moc/symbols.h +++ b/src/tools/moc/symbols.h @@ -46,6 +46,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -128,9 +129,61 @@ struct Symbol }; Q_DECLARE_TYPEINFO(Symbol, Q_MOVABLE_TYPE); - typedef QVector Symbols; +struct SafeSymbols { + Symbols symbols; + QByteArray expandedMacro; + int index; +}; + +class SymbolStack : public QStack +{ +public: + inline bool hasNext() { + while (!isEmpty() && top().index >= top().symbols.size()) + pop(); + return !isEmpty(); + } + inline Token next() { + while (!isEmpty() && top().index >= top().symbols.size()) + pop(); + if (isEmpty()) + return NOTOKEN; + return top().symbols.at(top().index++).token; + } + bool test(Token); + inline const Symbol &symbol() const { return top().symbols.at(top().index-1); } + inline Token token() { return symbol().token; } + inline QByteArray lexem() const { return symbol().lexem(); } + inline QByteArray unquotedLexem() { return symbol().unquotedLexem(); } + + bool dontReplaceSymbol(const QByteArray &name); +}; + +inline bool SymbolStack::test(Token token) +{ + int stackPos = size() - 1; + while (stackPos >= 0 && at(stackPos).index >= at(stackPos).symbols.size()) + --stackPos; + if (stackPos < 0) + return false; + if (at(stackPos).symbols.at(at(stackPos).index).token == token) { + next(); + return true; + } + return false; +} + +inline bool SymbolStack::dontReplaceSymbol(const QByteArray &name) +{ + for (int i = 0; i < size(); ++i) { + if (name == at(i).expandedMacro) + return true; + } + return false; +} + QT_END_NAMESPACE #endif // SYMBOLS_H -- cgit v1.2.3