preprocessor: Fix use after free when #undef the macro being invoked

BUG=chromium:648031
BUG=angleproject:1522

Change-Id: I825cea9e736a2c99133408249cfcd525431d31de
Reviewed-on: https://chromium-review.googlesource.com/386853
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/preprocessor/DiagnosticsBase.cpp b/src/compiler/preprocessor/DiagnosticsBase.cpp
index 9f62a9e..3281206 100644
--- a/src/compiler/preprocessor/DiagnosticsBase.cpp
+++ b/src/compiler/preprocessor/DiagnosticsBase.cpp
@@ -74,6 +74,8 @@
         return "predefined macro undefined";
       case PP_MACRO_UNTERMINATED_INVOCATION:
         return "unterminated macro invocation";
+      case PP_MACRO_UNDEFINED_WHILE_INVOKED:
+          return "macro undefined while being invoked";
       case PP_MACRO_TOO_FEW_ARGS:
         return "Not enough arguments for macro";
       case PP_MACRO_TOO_MANY_ARGS:
diff --git a/src/compiler/preprocessor/DiagnosticsBase.h b/src/compiler/preprocessor/DiagnosticsBase.h
index a089920..6c3fe9e 100644
--- a/src/compiler/preprocessor/DiagnosticsBase.h
+++ b/src/compiler/preprocessor/DiagnosticsBase.h
@@ -44,6 +44,7 @@
         PP_MACRO_PREDEFINED_REDEFINED,
         PP_MACRO_PREDEFINED_UNDEFINED,
         PP_MACRO_UNTERMINATED_INVOCATION,
+        PP_MACRO_UNDEFINED_WHILE_INVOKED,
         PP_MACRO_TOO_FEW_ARGS,
         PP_MACRO_TOO_MANY_ARGS,
         PP_MACRO_DUPLICATE_PARAMETER_NAMES,
diff --git a/src/compiler/preprocessor/DirectiveParser.cpp b/src/compiler/preprocessor/DirectiveParser.cpp
index aceb3b2..ebe72f1 100644
--- a/src/compiler/preprocessor/DirectiveParser.cpp
+++ b/src/compiler/preprocessor/DirectiveParser.cpp
@@ -445,6 +445,13 @@
         {
             mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED,
                                  token->location, token->text);
+            return;
+        }
+        else if (iter->second.expansionCount > 0)
+        {
+            mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location,
+                                 token->text);
+            return;
         }
         else
         {
diff --git a/src/compiler/preprocessor/Macro.h b/src/compiler/preprocessor/Macro.h
index 31ee22c..557df16 100644
--- a/src/compiler/preprocessor/Macro.h
+++ b/src/compiler/preprocessor/Macro.h
@@ -26,16 +26,12 @@
     typedef std::vector<std::string> Parameters;
     typedef std::vector<Token> Replacements;
 
-    Macro()
-        : predefined(false),
-          disabled(false),
-          type(kTypeObj)
-    {
-    }
+    Macro() : predefined(false), disabled(false), expansionCount(0), type(kTypeObj) {}
     bool equals(const Macro &other) const;
 
     bool predefined;
     mutable bool disabled;
+    mutable int expansionCount;
 
     Type type;
     std::string name;
diff --git a/src/compiler/preprocessor/MacroExpander.cpp b/src/compiler/preprocessor/MacroExpander.cpp
index 3c0423e..51b69f8 100644
--- a/src/compiler/preprocessor/MacroExpander.cpp
+++ b/src/compiler/preprocessor/MacroExpander.cpp
@@ -151,6 +151,8 @@
     assert(identifier.type == Token::IDENTIFIER);
     assert(identifier.text == macro.name);
 
+    macro.expansionCount++;
+
     std::vector<Token> replacements;
     if (!expandMacro(macro, identifier, &replacements))
         return false;
@@ -174,7 +176,9 @@
 
     assert(context->empty());
     assert(context->macro->disabled);
+    assert(context->macro->expansionCount > 0);
     context->macro->disabled = false;
+    context->macro->expansionCount--;
     delete context;
 }