Commit 47c27e82 by Olli Etuaho Committed by Commit Bot

Manage preprocessor Macro objects with shared pointers

This ensures that pointers to Macros that are removed from the macro set stay valid. Pointers to undef'd macros may need to be referred to if reenabling the macros has been deferred. BUG=chromium:681324 TEST=angle_unittests Change-Id: Ibbbabbcbd6b0a84254cda717ae63712e6d404ebd Reviewed-on: https://chromium-review.googlesource.com/427948Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 16c745a3
...@@ -131,7 +131,7 @@ bool hasDoubleUnderscores(const std::string &name) ...@@ -131,7 +131,7 @@ bool hasDoubleUnderscores(const std::string &name)
bool isMacroPredefined(const std::string &name, const pp::MacroSet &macroSet) bool isMacroPredefined(const std::string &name, const pp::MacroSet &macroSet)
{ {
pp::MacroSet::const_iterator iter = macroSet.find(name); pp::MacroSet::const_iterator iter = macroSet.find(name);
return iter != macroSet.end() ? iter->second.predefined : false; return iter != macroSet.end() ? iter->second->predefined : false;
} }
} // namespace anonymous } // namespace anonymous
...@@ -358,30 +358,30 @@ void DirectiveParser::parseDefine(Token *token) ...@@ -358,30 +358,30 @@ void DirectiveParser::parseDefine(Token *token)
token->text); token->text);
} }
Macro macro; std::shared_ptr<Macro> macro = std::make_shared<Macro>();
macro.type = Macro::kTypeObj; macro->type = Macro::kTypeObj;
macro.name = token->text; macro->name = token->text;
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type == '(' && !token->hasLeadingSpace()) if (token->type == '(' && !token->hasLeadingSpace())
{ {
// Function-like macro. Collect arguments. // Function-like macro. Collect arguments.
macro.type = Macro::kTypeFunc; macro->type = Macro::kTypeFunc;
do do
{ {
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
break; break;
if (std::find(macro.parameters.begin(), macro.parameters.end(), token->text) != if (std::find(macro->parameters.begin(), macro->parameters.end(), token->text) !=
macro.parameters.end()) macro->parameters.end())
{ {
mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES, mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES,
token->location, token->text); token->location, token->text);
return; return;
} }
macro.parameters.push_back(token->text); macro->parameters.push_back(token->text);
mTokenizer->lex(token); // Get ','. mTokenizer->lex(token); // Get ','.
} while (token->type == ','); } while (token->type == ',');
...@@ -400,24 +400,24 @@ void DirectiveParser::parseDefine(Token *token) ...@@ -400,24 +400,24 @@ void DirectiveParser::parseDefine(Token *token)
// list. Resetting it also allows us to reuse Token::equals() to // list. Resetting it also allows us to reuse Token::equals() to
// compare macros. // compare macros.
token->location = SourceLocation(); token->location = SourceLocation();
macro.replacements.push_back(*token); macro->replacements.push_back(*token);
mTokenizer->lex(token); mTokenizer->lex(token);
} }
if (!macro.replacements.empty()) if (!macro->replacements.empty())
{ {
// Whitespace preceding the replacement list is not considered part of // Whitespace preceding the replacement list is not considered part of
// the replacement list for either form of macro. // the replacement list for either form of macro.
macro.replacements.front().setHasLeadingSpace(false); macro->replacements.front().setHasLeadingSpace(false);
} }
// Check for macro redefinition. // Check for macro redefinition.
MacroSet::const_iterator iter = mMacroSet->find(macro.name); MacroSet::const_iterator iter = mMacroSet->find(macro->name);
if (iter != mMacroSet->end() && !macro.equals(iter->second)) if (iter != mMacroSet->end() && !macro->equals(*iter->second))
{ {
mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED, token->location, macro.name); mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED, token->location, macro->name);
return; return;
} }
mMacroSet->insert(std::make_pair(macro.name, macro)); mMacroSet->insert(std::make_pair(macro->name, macro));
} }
void DirectiveParser::parseUndef(Token *token) void DirectiveParser::parseUndef(Token *token)
...@@ -434,13 +434,13 @@ void DirectiveParser::parseUndef(Token *token) ...@@ -434,13 +434,13 @@ void DirectiveParser::parseUndef(Token *token)
MacroSet::iterator iter = mMacroSet->find(token->text); MacroSet::iterator iter = mMacroSet->find(token->text);
if (iter != mMacroSet->end()) if (iter != mMacroSet->end())
{ {
if (iter->second.predefined) if (iter->second->predefined)
{ {
mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED, token->location, mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED, token->location,
token->text); token->text);
return; return;
} }
else if (iter->second.expansionCount > 0) else if (iter->second->expansionCount > 0)
{ {
mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location, mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location,
token->text); token->text);
......
...@@ -24,11 +24,11 @@ void PredefineMacro(MacroSet *macroSet, const char *name, int value) ...@@ -24,11 +24,11 @@ void PredefineMacro(MacroSet *macroSet, const char *name, int value)
token.type = Token::CONST_INT; token.type = Token::CONST_INT;
token.text = ToString(value); token.text = ToString(value);
Macro macro; std::shared_ptr<Macro> macro = std::make_shared<Macro>();
macro.predefined = true; macro->predefined = true;
macro.type = Macro::kTypeObj; macro->type = Macro::kTypeObj;
macro.name = name; macro->name = name;
macro.replacements.push_back(token); macro->replacements.push_back(token);
(*macroSet)[name] = macro; (*macroSet)[name] = macro;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define COMPILER_PREPROCESSOR_MACRO_H_ #define COMPILER_PREPROCESSOR_MACRO_H_
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -39,7 +40,7 @@ struct Macro ...@@ -39,7 +40,7 @@ struct Macro
Replacements replacements; Replacements replacements;
}; };
typedef std::map<std::string, Macro> MacroSet; typedef std::map<std::string, std::shared_ptr<Macro>> MacroSet;
void PredefineMacro(MacroSet *macroSet, const char *name, int value); void PredefineMacro(MacroSet *macroSet, const char *name, int value);
......
...@@ -70,8 +70,11 @@ MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expande ...@@ -70,8 +70,11 @@ MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expande
MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler() MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
{ {
mExpander->mDeferReenablingMacros = false; mExpander->mDeferReenablingMacros = false;
for (auto *macro : mExpander->mMacrosToReenable) for (auto macro : mExpander->mMacrosToReenable)
{ {
// Copying the string here by using substr is a check for use-after-free. It detects
// use-after-free more reliably than just toggling the disabled flag.
ASSERT(macro->name.substr() != "");
macro->disabled = false; macro->disabled = false;
} }
mExpander->mMacrosToReenable.clear(); mExpander->mMacrosToReenable.clear();
...@@ -115,8 +118,8 @@ void MacroExpander::lex(Token *token) ...@@ -115,8 +118,8 @@ void MacroExpander::lex(Token *token)
if (iter == mMacroSet->end()) if (iter == mMacroSet->end())
break; break;
const Macro &macro = iter->second; std::shared_ptr<Macro> macro = iter->second;
if (macro.disabled) if (macro->disabled)
{ {
// If a particular token is not expanded, it is never expanded. // If a particular token is not expanded, it is never expanded.
token->setExpansionDisabled(true); token->setExpansionDisabled(true);
...@@ -125,12 +128,12 @@ void MacroExpander::lex(Token *token) ...@@ -125,12 +128,12 @@ void MacroExpander::lex(Token *token)
// Bump the expansion count before peeking if the next token is a '(' // Bump the expansion count before peeking if the next token is a '('
// otherwise there could be a #undef of the macro before the next token. // otherwise there could be a #undef of the macro before the next token.
macro.expansionCount++; macro->expansionCount++;
if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen()) if ((macro->type == Macro::kTypeFunc) && !isNextTokenLeftParen())
{ {
// If the token immediately after the macro name is not a '(', // If the token immediately after the macro name is not a '(',
// this macro should not be expanded. // this macro should not be expanded.
macro.expansionCount--; macro->expansionCount--;
break; break;
} }
...@@ -190,22 +193,22 @@ bool MacroExpander::isNextTokenLeftParen() ...@@ -190,22 +193,22 @@ bool MacroExpander::isNextTokenLeftParen()
return lparen; return lparen;
} }
bool MacroExpander::pushMacro(const Macro &macro, const Token &identifier) bool MacroExpander::pushMacro(std::shared_ptr<Macro> macro, const Token &identifier)
{ {
ASSERT(!macro.disabled); ASSERT(!macro->disabled);
ASSERT(!identifier.expansionDisabled()); ASSERT(!identifier.expansionDisabled());
ASSERT(identifier.type == Token::IDENTIFIER); ASSERT(identifier.type == Token::IDENTIFIER);
ASSERT(identifier.text == macro.name); ASSERT(identifier.text == macro->name);
std::vector<Token> replacements; std::vector<Token> replacements;
if (!expandMacro(macro, identifier, &replacements)) if (!expandMacro(*macro, identifier, &replacements))
return false; return false;
// Macro is disabled for expansion until it is popped off the stack. // Macro is disabled for expansion until it is popped off the stack.
macro.disabled = true; macro->disabled = true;
MacroContext *context = new MacroContext; MacroContext *context = new MacroContext;
context->macro = &macro; context->macro = macro;
context->replacements.swap(replacements); context->replacements.swap(replacements);
mContextStack.push_back(context); mContextStack.push_back(context);
mTotalTokensInContexts += context->replacements.size(); mTotalTokensInContexts += context->replacements.size();
......
...@@ -35,7 +35,7 @@ class MacroExpander : public Lexer ...@@ -35,7 +35,7 @@ class MacroExpander : public Lexer
void ungetToken(const Token &token); void ungetToken(const Token &token);
bool isNextTokenLeftParen(); bool isNextTokenLeftParen();
bool pushMacro(const Macro &macro, const Token &identifier); bool pushMacro(std::shared_ptr<Macro> macro, const Token &identifier);
void popMacro(); void popMacro();
bool expandMacro(const Macro &macro, const Token &identifier, std::vector<Token> *replacements); bool expandMacro(const Macro &macro, const Token &identifier, std::vector<Token> *replacements);
...@@ -56,7 +56,7 @@ class MacroExpander : public Lexer ...@@ -56,7 +56,7 @@ class MacroExpander : public Lexer
const Token &get(); const Token &get();
void unget(); void unget();
const Macro *macro; std::shared_ptr<Macro> macro;
std::size_t index; std::size_t index;
std::vector<Token> replacements; std::vector<Token> replacements;
}; };
...@@ -72,7 +72,7 @@ class MacroExpander : public Lexer ...@@ -72,7 +72,7 @@ class MacroExpander : public Lexer
int mAllowedMacroExpansionDepth; int mAllowedMacroExpansionDepth;
bool mDeferReenablingMacros; bool mDeferReenablingMacros;
std::vector<const Macro *> mMacrosToReenable; std::vector<std::shared_ptr<Macro>> mMacrosToReenable;
class ScopedMacroReenabler; class ScopedMacroReenabler;
}; };
......
...@@ -985,6 +985,27 @@ TEST_F(DefineTest, RecursiveMacroNameInsideIncompleteMacroInvocationInMacroExpan ...@@ -985,6 +985,27 @@ TEST_F(DefineTest, RecursiveMacroNameInsideIncompleteMacroInvocationInMacroExpan
preprocess(input, expected); preprocess(input, expected);
} }
// The name of the macro "a" is inside an incomplete macro invocation of macro "m()" in its own
// expansion. Then the macro "a" is undef'd. This is a regression test for a memory management bug
// where macro "a" would be freed on undef even though cleaning up the recursive macro invocation
// would still need to refer to macro "a".
TEST_F(DefineTest, UndefInsideRecursiveMacroInvocation)
{
const char *input =
"#define m(a)\n"
"#define a m((a)\n"
"a\n"
"#undef a\n"
")\n";
const char *expected =
"\n"
"\n"
"\n"
"\n"
"\n";
preprocess(input, expected);
}
// The macro invocations form a long chain. The macro expander should protect against stack overflow // The macro invocations form a long chain. The macro expander should protect against stack overflow
// and generate an error in this case. // and generate an error in this case.
TEST_F(DefineTest, LongMacroInvocationChain) TEST_F(DefineTest, LongMacroInvocationChain)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment