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/427948
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/preprocessor/DirectiveParser.cpp b/src/compiler/preprocessor/DirectiveParser.cpp
index b366a32..9cda720 100644
--- a/src/compiler/preprocessor/DirectiveParser.cpp
+++ b/src/compiler/preprocessor/DirectiveParser.cpp
@@ -131,7 +131,7 @@
 bool isMacroPredefined(const std::string &name, const pp::MacroSet &macroSet)
 {
     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
@@ -358,30 +358,30 @@
                              token->text);
     }
 
-    Macro macro;
-    macro.type = Macro::kTypeObj;
-    macro.name = token->text;
+    std::shared_ptr<Macro> macro = std::make_shared<Macro>();
+    macro->type                  = Macro::kTypeObj;
+    macro->name                  = token->text;
 
     mTokenizer->lex(token);
     if (token->type == '(' && !token->hasLeadingSpace())
     {
         // Function-like macro. Collect arguments.
-        macro.type = Macro::kTypeFunc;
+        macro->type = Macro::kTypeFunc;
         do
         {
             mTokenizer->lex(token);
             if (token->type != Token::IDENTIFIER)
                 break;
 
-            if (std::find(macro.parameters.begin(), macro.parameters.end(), token->text) !=
-                macro.parameters.end())
+            if (std::find(macro->parameters.begin(), macro->parameters.end(), token->text) !=
+                macro->parameters.end())
             {
                 mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES,
                                      token->location, token->text);
                 return;
             }
 
-            macro.parameters.push_back(token->text);
+            macro->parameters.push_back(token->text);
 
             mTokenizer->lex(token);  // Get ','.
         } while (token->type == ',');
@@ -400,24 +400,24 @@
         // list. Resetting it also allows us to reuse Token::equals() to
         // compare macros.
         token->location = SourceLocation();
-        macro.replacements.push_back(*token);
+        macro->replacements.push_back(*token);
         mTokenizer->lex(token);
     }
-    if (!macro.replacements.empty())
+    if (!macro->replacements.empty())
     {
         // Whitespace preceding the replacement list is not considered part of
         // the replacement list for either form of macro.
-        macro.replacements.front().setHasLeadingSpace(false);
+        macro->replacements.front().setHasLeadingSpace(false);
     }
 
     // Check for macro redefinition.
-    MacroSet::const_iterator iter = mMacroSet->find(macro.name);
-    if (iter != mMacroSet->end() && !macro.equals(iter->second))
+    MacroSet::const_iterator iter = mMacroSet->find(macro->name);
+    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;
     }
-    mMacroSet->insert(std::make_pair(macro.name, macro));
+    mMacroSet->insert(std::make_pair(macro->name, macro));
 }
 
 void DirectiveParser::parseUndef(Token *token)
@@ -434,13 +434,13 @@
     MacroSet::iterator iter = mMacroSet->find(token->text);
     if (iter != mMacroSet->end())
     {
-        if (iter->second.predefined)
+        if (iter->second->predefined)
         {
             mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED, token->location,
                                  token->text);
             return;
         }
-        else if (iter->second.expansionCount > 0)
+        else if (iter->second->expansionCount > 0)
         {
             mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location,
                                  token->text);
diff --git a/src/compiler/preprocessor/Macro.cpp b/src/compiler/preprocessor/Macro.cpp
index e7f2bae..6162767 100644
--- a/src/compiler/preprocessor/Macro.cpp
+++ b/src/compiler/preprocessor/Macro.cpp
@@ -24,11 +24,11 @@
     token.type = Token::CONST_INT;
     token.text = ToString(value);
 
-    Macro macro;
-    macro.predefined = true;
-    macro.type       = Macro::kTypeObj;
-    macro.name       = name;
-    macro.replacements.push_back(token);
+    std::shared_ptr<Macro> macro = std::make_shared<Macro>();
+    macro->predefined            = true;
+    macro->type                  = Macro::kTypeObj;
+    macro->name                  = name;
+    macro->replacements.push_back(token);
 
     (*macroSet)[name] = macro;
 }
diff --git a/src/compiler/preprocessor/Macro.h b/src/compiler/preprocessor/Macro.h
index 557df16..f835c42 100644
--- a/src/compiler/preprocessor/Macro.h
+++ b/src/compiler/preprocessor/Macro.h
@@ -8,6 +8,7 @@
 #define COMPILER_PREPROCESSOR_MACRO_H_
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -39,7 +40,7 @@
     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);
 
diff --git a/src/compiler/preprocessor/MacroExpander.cpp b/src/compiler/preprocessor/MacroExpander.cpp
index 1c1958d..cc115f9 100644
--- a/src/compiler/preprocessor/MacroExpander.cpp
+++ b/src/compiler/preprocessor/MacroExpander.cpp
@@ -70,8 +70,11 @@
 MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
 {
     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;
     }
     mExpander->mMacrosToReenable.clear();
@@ -115,8 +118,8 @@
         if (iter == mMacroSet->end())
             break;
 
-        const Macro &macro = iter->second;
-        if (macro.disabled)
+        std::shared_ptr<Macro> macro = iter->second;
+        if (macro->disabled)
         {
             // If a particular token is not expanded, it is never expanded.
             token->setExpansionDisabled(true);
@@ -125,12 +128,12 @@
 
         // 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.
-        macro.expansionCount++;
-        if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
+        macro->expansionCount++;
+        if ((macro->type == Macro::kTypeFunc) && !isNextTokenLeftParen())
         {
             // If the token immediately after the macro name is not a '(',
             // this macro should not be expanded.
-            macro.expansionCount--;
+            macro->expansionCount--;
             break;
         }
 
@@ -190,22 +193,22 @@
     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.type == Token::IDENTIFIER);
-    ASSERT(identifier.text == macro.name);
+    ASSERT(identifier.text == macro->name);
 
     std::vector<Token> replacements;
-    if (!expandMacro(macro, identifier, &replacements))
+    if (!expandMacro(*macro, identifier, &replacements))
         return false;
 
     // Macro is disabled for expansion until it is popped off the stack.
-    macro.disabled = true;
+    macro->disabled = true;
 
     MacroContext *context = new MacroContext;
-    context->macro        = &macro;
+    context->macro        = macro;
     context->replacements.swap(replacements);
     mContextStack.push_back(context);
     mTotalTokensInContexts += context->replacements.size();
diff --git a/src/compiler/preprocessor/MacroExpander.h b/src/compiler/preprocessor/MacroExpander.h
index ef52c5e..7a4f5a9 100644
--- a/src/compiler/preprocessor/MacroExpander.h
+++ b/src/compiler/preprocessor/MacroExpander.h
@@ -35,7 +35,7 @@
     void ungetToken(const Token &token);
     bool isNextTokenLeftParen();
 
-    bool pushMacro(const Macro &macro, const Token &identifier);
+    bool pushMacro(std::shared_ptr<Macro> macro, const Token &identifier);
     void popMacro();
 
     bool expandMacro(const Macro &macro, const Token &identifier, std::vector<Token> *replacements);
@@ -56,7 +56,7 @@
         const Token &get();
         void unget();
 
-        const Macro *macro;
+        std::shared_ptr<Macro> macro;
         std::size_t index;
         std::vector<Token> replacements;
     };
@@ -72,7 +72,7 @@
     int mAllowedMacroExpansionDepth;
 
     bool mDeferReenablingMacros;
-    std::vector<const Macro *> mMacrosToReenable;
+    std::vector<std::shared_ptr<Macro>> mMacrosToReenable;
 
     class ScopedMacroReenabler;
 };