Support parsing defined operator generated by macro expansion

dEQP tests enforce that the defined operator should be parsed even when
it is generated as a result of macro expansion, even though this is
undefined according to the C++ preprocessor spec.

Implement support for this by putting the parsing for the defined
operator inside MacroExpander. The operator gets processed right after
it is generated by macro expansion. Parsing the defined operator is
toggled with a boolean according to the context where MacroExpander
is used.

BUG=angleproject:989
TEST=angle_unittests,
     dEQP-GLES3.functional.shaders.preprocessor.* - 2 tests start passing:
     dEQP-GLES3.functional.shaders.preprocessor.conditional_inclusion.basic_2*

Change-Id: I780e63bd4558253657d898685d62339017564a06
Reviewed-on: https://chromium-review.googlesource.com/300970
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/preprocessor/DirectiveParser.cpp b/src/compiler/preprocessor/DirectiveParser.cpp
index 1c53289..041d6ca 100644
--- a/src/compiler/preprocessor/DirectiveParser.cpp
+++ b/src/compiler/preprocessor/DirectiveParser.cpp
@@ -141,71 +141,6 @@
 namespace pp
 {
 
-class DefinedParser : public Lexer
-{
-  public:
-    DefinedParser(Lexer *lexer,
-                  const MacroSet *macroSet,
-                  Diagnostics *diagnostics)
-        : mLexer(lexer),
-          mMacroSet(macroSet),
-          mDiagnostics(diagnostics)
-    {
-    }
-
-  protected:
-    void lex(Token *token) override
-    {
-        const char kDefined[] = "defined";
-
-        mLexer->lex(token);
-        if (token->type != Token::IDENTIFIER)
-            return;
-        if (token->text != kDefined)
-            return;
-
-        bool paren = false;
-        mLexer->lex(token);
-        if (token->type == '(')
-        {
-            paren = true;
-            mLexer->lex(token);
-        }
-
-        if (token->type != Token::IDENTIFIER)
-        {
-            mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
-                                 token->location, token->text);
-            skipUntilEOD(mLexer, token);
-            return;
-        }
-        MacroSet::const_iterator iter = mMacroSet->find(token->text);
-        std::string expression = iter != mMacroSet->end() ? "1" : "0";
-
-        if (paren)
-        {
-            mLexer->lex(token);
-            if (token->type != ')')
-            {
-                mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
-                                     token->location, token->text);
-                skipUntilEOD(mLexer, token);
-                return;
-            }
-        }
-
-        // We have a valid defined operator.
-        // Convert the current token into a CONST_INT token.
-        token->type = Token::CONST_INT;
-        token->text = expression;
-    }
-
-  private:
-    Lexer *mLexer;
-    const MacroSet *mMacroSet;
-    Diagnostics *mDiagnostics;
-};
-
 DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
                                  MacroSet *macroSet,
                                  Diagnostics *diagnostics,
@@ -839,7 +774,7 @@
     int line = 0, file = 0;
     int state = LINE_NUMBER;
 
-    MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics);
+    MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, false);
     macroExpander.lex(token);
     while ((token->type != '\n') && (token->type != Token::LAST))
     {
@@ -954,8 +889,7 @@
     assert((getDirective(token) == DIRECTIVE_IF) ||
            (getDirective(token) == DIRECTIVE_ELIF));
 
-    DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics);
-    MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics);
+    MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, true);
     ExpressionParser expressionParser(&macroExpander, mDiagnostics);
 
     int expression = 0;
diff --git a/src/compiler/preprocessor/MacroExpander.cpp b/src/compiler/preprocessor/MacroExpander.cpp
index 36458ff..e878ee3 100644
--- a/src/compiler/preprocessor/MacroExpander.cpp
+++ b/src/compiler/preprocessor/MacroExpander.cpp
@@ -48,10 +48,9 @@
 
 MacroExpander::MacroExpander(Lexer *lexer,
                              MacroSet *macroSet,
-                             Diagnostics *diagnostics)
-    : mLexer(lexer),
-      mMacroSet(macroSet),
-      mDiagnostics(diagnostics)
+                             Diagnostics *diagnostics,
+                             bool parseDefined)
+    : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mParseDefined(parseDefined)
 {
 }
 
@@ -67,11 +66,54 @@
 {
     while (true)
     {
+        const char kDefined[] = "defined";
+
         getToken(token);
 
         if (token->type != Token::IDENTIFIER)
             break;
 
+        // Defined operator is parsed here since it may be generated by macro expansion.
+        // Defined operator produced by macro expansion has undefined behavior according to C++
+        // spec, which the GLSL spec references (see C++14 draft spec section 16.1.4), but this
+        // behavior is needed for passing dEQP tests, which enforce stricter compatibility between
+        // implementations.
+        if (mParseDefined && token->text == kDefined)
+        {
+            bool paren = false;
+            getToken(token);
+            if (token->type == '(')
+            {
+                paren = true;
+                getToken(token);
+            }
+            if (token->type != Token::IDENTIFIER)
+            {
+                mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+                                     token->text);
+                break;
+            }
+            auto iter              = mMacroSet->find(token->text);
+            std::string expression = iter != mMacroSet->end() ? "1" : "0";
+
+            if (paren)
+            {
+                getToken(token);
+                if (token->type != ')')
+                {
+                    mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+                                         token->text);
+                    break;
+                }
+            }
+
+            // We have a valid defined operator.
+            // Convert the current token into a CONST_INT token.
+            token->type = Token::CONST_INT;
+            token->text = expression;
+            break;
+        }
+
         if (token->expansionDisabled())
             break;
 
@@ -325,7 +367,7 @@
     {
         MacroArg &arg = args->at(i);
         TokenLexer lexer(&arg);
-        MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
+        MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mParseDefined);
 
         arg.clear();
         expander.lex(&token);
diff --git a/src/compiler/preprocessor/MacroExpander.h b/src/compiler/preprocessor/MacroExpander.h
index 33e38da..3cc860d 100644
--- a/src/compiler/preprocessor/MacroExpander.h
+++ b/src/compiler/preprocessor/MacroExpander.h
@@ -24,7 +24,7 @@
 class MacroExpander : public Lexer
 {
   public:
-    MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics);
+    MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics, bool parseDefined);
     ~MacroExpander() override;
 
     void lex(Token *token) override;
@@ -81,6 +81,7 @@
     Lexer *mLexer;
     MacroSet *mMacroSet;
     Diagnostics *mDiagnostics;
+    bool mParseDefined;
 
     std::auto_ptr<Token> mReserveToken;
     std::vector<MacroContext *> mContextStack;
diff --git a/src/compiler/preprocessor/Preprocessor.cpp b/src/compiler/preprocessor/Preprocessor.cpp
index 2850ca1..aeb9c46 100644
--- a/src/compiler/preprocessor/Preprocessor.cpp
+++ b/src/compiler/preprocessor/Preprocessor.cpp
@@ -26,12 +26,11 @@
     DirectiveParser directiveParser;
     MacroExpander macroExpander;
 
-    PreprocessorImpl(Diagnostics *diag,
-                     DirectiveHandler *directiveHandler)
+    PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler)
         : diagnostics(diag),
           tokenizer(diag),
           directiveParser(&tokenizer, &macroSet, diag, directiveHandler),
-          macroExpander(&directiveParser, &macroSet, diag)
+          macroExpander(&directiveParser, &macroSet, diag, false)
     {
     }
 };