LCOV - code coverage report
Current view: top level - gfx/angle/src/compiler/preprocessor - MacroExpander.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 226 0.4 %
Date: 2017-07-14 16:53:18 Functions: 2 24 8.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //
       2             : // Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
       3             : // Use of this source code is governed by a BSD-style license that can be
       4             : // found in the LICENSE file.
       5             : //
       6             : 
       7             : #include "compiler/preprocessor/MacroExpander.h"
       8             : 
       9             : #include <algorithm>
      10             : 
      11             : #include "common/debug.h"
      12             : #include "compiler/preprocessor/DiagnosticsBase.h"
      13             : #include "compiler/preprocessor/Token.h"
      14             : 
      15             : namespace pp
      16             : {
      17             : 
      18             : namespace
      19             : {
      20             : 
      21             : const size_t kMaxContextTokens = 10000;
      22             : 
      23           0 : class TokenLexer : public Lexer
      24             : {
      25             :  public:
      26             :     typedef std::vector<Token> TokenVector;
      27             : 
      28           0 :     TokenLexer(TokenVector *tokens)
      29           0 :     {
      30           0 :         tokens->swap(mTokens);
      31           0 :         mIter = mTokens.begin();
      32           0 :     }
      33             : 
      34           0 :     void lex(Token *token) override
      35             :     {
      36           0 :         if (mIter == mTokens.end())
      37             :         {
      38           0 :             token->reset();
      39           0 :             token->type = Token::LAST;
      40             :         }
      41             :         else
      42             :         {
      43           0 :             *token = *mIter++;
      44             :         }
      45           0 :     }
      46             : 
      47             :  private:
      48             :     TokenVector mTokens;
      49             :     TokenVector::const_iterator mIter;
      50             : };
      51             : 
      52             : }  // anonymous namespace
      53             : 
      54             : class MacroExpander::ScopedMacroReenabler final : angle::NonCopyable
      55             : {
      56             :   public:
      57             :     ScopedMacroReenabler(MacroExpander *expander);
      58             :     ~ScopedMacroReenabler();
      59             : 
      60             :   private:
      61             :     MacroExpander *mExpander;
      62             : };
      63             : 
      64           0 : MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expander)
      65           0 :     : mExpander(expander)
      66             : {
      67           0 :     mExpander->mDeferReenablingMacros = true;
      68           0 : }
      69             : 
      70           0 : MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
      71             : {
      72           0 :     mExpander->mDeferReenablingMacros = false;
      73           0 :     for (auto *macro : mExpander->mMacrosToReenable)
      74             :     {
      75           0 :         macro->disabled = false;
      76             :     }
      77           0 :     mExpander->mMacrosToReenable.clear();
      78           0 : }
      79             : 
      80           0 : MacroExpander::MacroExpander(Lexer *lexer,
      81             :                              MacroSet *macroSet,
      82             :                              Diagnostics *diagnostics,
      83           0 :                              int allowedMacroExpansionDepth)
      84             :     : mLexer(lexer),
      85             :       mMacroSet(macroSet),
      86             :       mDiagnostics(diagnostics),
      87             :       mTotalTokensInContexts(0),
      88             :       mAllowedMacroExpansionDepth(allowedMacroExpansionDepth),
      89           0 :       mDeferReenablingMacros(false)
      90             : {
      91           0 : }
      92             : 
      93           0 : MacroExpander::~MacroExpander()
      94             : {
      95           0 :     ASSERT(mMacrosToReenable.empty());
      96           0 :     for (MacroContext *context : mContextStack)
      97             :     {
      98           0 :         delete context;
      99             :     }
     100           0 : }
     101             : 
     102           0 : void MacroExpander::lex(Token *token)
     103             : {
     104             :     while (true)
     105             :     {
     106           0 :         getToken(token);
     107             : 
     108           0 :         if (token->type != Token::IDENTIFIER)
     109           0 :             break;
     110             : 
     111           0 :         if (token->expansionDisabled())
     112           0 :             break;
     113             : 
     114           0 :         MacroSet::const_iterator iter = mMacroSet->find(token->text);
     115           0 :         if (iter == mMacroSet->end())
     116           0 :             break;
     117             : 
     118           0 :         const Macro& macro = iter->second;
     119           0 :         if (macro.disabled)
     120             :         {
     121             :             // If a particular token is not expanded, it is never expanded.
     122           0 :             token->setExpansionDisabled(true);
     123           0 :             break;
     124             :         }
     125             : 
     126             :         // Bump the expansion count before peeking if the next token is a '('
     127             :         // otherwise there could be a #undef of the macro before the next token.
     128           0 :         macro.expansionCount++;
     129           0 :         if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
     130             :         {
     131             :             // If the token immediately after the macro name is not a '(',
     132             :             // this macro should not be expanded.
     133           0 :             macro.expansionCount--;
     134           0 :             break;
     135             :         }
     136             : 
     137           0 :         pushMacro(macro, *token);
     138           0 :     }
     139           0 : }
     140             : 
     141           0 : void MacroExpander::getToken(Token *token)
     142             : {
     143           0 :     if (mReserveToken.get())
     144             :     {
     145           0 :         *token = *mReserveToken;
     146           0 :         mReserveToken.reset();
     147           0 :         return;
     148             :     }
     149             : 
     150             :     // First pop all empty macro contexts.
     151           0 :     while (!mContextStack.empty() && mContextStack.back()->empty())
     152             :     {
     153           0 :         popMacro();
     154             :     }
     155             : 
     156           0 :     if (!mContextStack.empty())
     157             :     {
     158           0 :         *token = mContextStack.back()->get();
     159             :     }
     160             :     else
     161             :     {
     162           0 :         ASSERT(mTotalTokensInContexts == 0);
     163           0 :         mLexer->lex(token);
     164             :     }
     165             : }
     166             : 
     167           0 : void MacroExpander::ungetToken(const Token &token)
     168             : {
     169           0 :     if (!mContextStack.empty())
     170             :     {
     171           0 :         MacroContext *context = mContextStack.back();
     172           0 :         context->unget();
     173           0 :         ASSERT(context->replacements[context->index] == token);
     174             :     }
     175             :     else
     176             :     {
     177           0 :         ASSERT(!mReserveToken.get());
     178           0 :         mReserveToken.reset(new Token(token));
     179             :     }
     180           0 : }
     181             : 
     182           0 : bool MacroExpander::isNextTokenLeftParen()
     183             : {
     184           0 :     Token token;
     185           0 :     getToken(&token);
     186             : 
     187           0 :     bool lparen = token.type == '(';
     188           0 :     ungetToken(token);
     189             : 
     190           0 :     return lparen;
     191             : }
     192             : 
     193           0 : bool MacroExpander::pushMacro(const Macro &macro, const Token &identifier)
     194             : {
     195           0 :     ASSERT(!macro.disabled);
     196           0 :     ASSERT(!identifier.expansionDisabled());
     197           0 :     ASSERT(identifier.type == Token::IDENTIFIER);
     198           0 :     ASSERT(identifier.text == macro.name);
     199             : 
     200           0 :     std::vector<Token> replacements;
     201           0 :     if (!expandMacro(macro, identifier, &replacements))
     202           0 :         return false;
     203             : 
     204             :     // Macro is disabled for expansion until it is popped off the stack.
     205           0 :     macro.disabled = true;
     206             : 
     207           0 :     MacroContext *context = new MacroContext;
     208           0 :     context->macro = &macro;
     209           0 :     context->replacements.swap(replacements);
     210           0 :     mContextStack.push_back(context);
     211           0 :     mTotalTokensInContexts += context->replacements.size();
     212           0 :     return true;
     213             : }
     214             : 
     215           0 : void MacroExpander::popMacro()
     216             : {
     217           0 :     ASSERT(!mContextStack.empty());
     218             : 
     219           0 :     MacroContext *context = mContextStack.back();
     220           0 :     mContextStack.pop_back();
     221             : 
     222           0 :     ASSERT(context->empty());
     223           0 :     ASSERT(context->macro->disabled);
     224           0 :     ASSERT(context->macro->expansionCount > 0);
     225           0 :     if (mDeferReenablingMacros)
     226             :     {
     227           0 :         mMacrosToReenable.push_back(context->macro);
     228             :     }
     229             :     else
     230             :     {
     231           0 :         context->macro->disabled = false;
     232             :     }
     233           0 :     context->macro->expansionCount--;
     234           0 :     mTotalTokensInContexts -= context->replacements.size();
     235           0 :     delete context;
     236           0 : }
     237             : 
     238           0 : bool MacroExpander::expandMacro(const Macro &macro,
     239             :                                 const Token &identifier,
     240             :                                 std::vector<Token> *replacements)
     241             : {
     242           0 :     replacements->clear();
     243             : 
     244             :     // In the case of an object-like macro, the replacement list gets its location
     245             :     // from the identifier, but in the case of a function-like macro, the replacement
     246             :     // list gets its location from the closing parenthesis of the macro invocation.
     247             :     // This is tested by dEQP-GLES3.functional.shaders.preprocessor.predefined_macros.*
     248           0 :     SourceLocation replacementLocation = identifier.location;
     249           0 :     if (macro.type == Macro::kTypeObj)
     250             :     {
     251           0 :         replacements->assign(macro.replacements.begin(),
     252           0 :                              macro.replacements.end());
     253             : 
     254           0 :         if (macro.predefined)
     255             :         {
     256           0 :             const char kLine[] = "__LINE__";
     257           0 :             const char kFile[] = "__FILE__";
     258             : 
     259           0 :             ASSERT(replacements->size() == 1);
     260           0 :             Token& repl = replacements->front();
     261           0 :             if (macro.name == kLine)
     262             :             {
     263           0 :                 repl.text = ToString(identifier.location.line);
     264             :             }
     265           0 :             else if (macro.name == kFile)
     266             :             {
     267           0 :                 repl.text = ToString(identifier.location.file);
     268             :             }
     269             :         }
     270             :     }
     271             :     else
     272             :     {
     273           0 :         ASSERT(macro.type == Macro::kTypeFunc);
     274           0 :         std::vector<MacroArg> args;
     275           0 :         args.reserve(macro.parameters.size());
     276           0 :         if (!collectMacroArgs(macro, identifier, &args, &replacementLocation))
     277           0 :             return false;
     278             : 
     279           0 :         replaceMacroParams(macro, args, replacements);
     280             :     }
     281             : 
     282           0 :     for (std::size_t i = 0; i < replacements->size(); ++i)
     283             :     {
     284           0 :         Token& repl = replacements->at(i);
     285           0 :         if (i == 0)
     286             :         {
     287             :             // The first token in the replacement list inherits the padding
     288             :             // properties of the identifier token.
     289           0 :             repl.setAtStartOfLine(identifier.atStartOfLine());
     290           0 :             repl.setHasLeadingSpace(identifier.hasLeadingSpace());
     291             :         }
     292           0 :         repl.location = replacementLocation;
     293             :     }
     294           0 :     return true;
     295             : }
     296             : 
     297           0 : bool MacroExpander::collectMacroArgs(const Macro &macro,
     298             :                                      const Token &identifier,
     299             :                                      std::vector<MacroArg> *args,
     300             :                                      SourceLocation *closingParenthesisLocation)
     301             : {
     302           0 :     Token token;
     303           0 :     getToken(&token);
     304           0 :     ASSERT(token.type == '(');
     305             : 
     306           0 :     args->push_back(MacroArg());
     307             : 
     308             :     // Defer reenabling macros until args collection is finished to avoid the possibility of
     309             :     // infinite recursion. Otherwise infinite recursion might happen when expanding the args after
     310             :     // macros have been popped from the context stack when parsing the args.
     311           0 :     ScopedMacroReenabler deferReenablingMacros(this);
     312             : 
     313           0 :     int openParens = 1;
     314           0 :     while (openParens != 0)
     315             :     {
     316           0 :         getToken(&token);
     317             : 
     318           0 :         if (token.type == Token::LAST)
     319             :         {
     320           0 :             mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
     321           0 :                                  identifier.location, identifier.text);
     322             :             // Do not lose EOF token.
     323           0 :             ungetToken(token);
     324           0 :             return false;
     325             :         }
     326             : 
     327           0 :         bool isArg = false; // True if token is part of the current argument.
     328           0 :         switch (token.type)
     329             :         {
     330             :           case '(':
     331           0 :             ++openParens;
     332           0 :             isArg = true;
     333           0 :             break;
     334             :           case ')':
     335           0 :             --openParens;
     336           0 :             isArg = openParens != 0;
     337           0 :             *closingParenthesisLocation = token.location;
     338           0 :             break;
     339             :           case ',':
     340             :             // The individual arguments are separated by comma tokens, but
     341             :             // the comma tokens between matching inner parentheses do not
     342             :             // seperate arguments.
     343           0 :             if (openParens == 1)
     344           0 :                 args->push_back(MacroArg());
     345           0 :             isArg = openParens != 1;
     346           0 :             break;
     347             :           default:
     348           0 :             isArg = true;
     349           0 :             break;
     350             :         }
     351           0 :         if (isArg)
     352             :         {
     353           0 :             MacroArg &arg = args->back();
     354             :             // Initial whitespace is not part of the argument.
     355           0 :             if (arg.empty())
     356           0 :                 token.setHasLeadingSpace(false);
     357           0 :             arg.push_back(token);
     358             :         }
     359             :     }
     360             : 
     361           0 :     const Macro::Parameters &params = macro.parameters;
     362             :     // If there is only one empty argument, it is equivalent to no argument.
     363           0 :     if (params.empty() && (args->size() == 1) && args->front().empty())
     364             :     {
     365           0 :         args->clear();
     366             :     }
     367             :     // Validate the number of arguments.
     368           0 :     if (args->size() != params.size())
     369             :     {
     370           0 :         Diagnostics::ID id = args->size() < macro.parameters.size() ?
     371             :             Diagnostics::PP_MACRO_TOO_FEW_ARGS :
     372           0 :             Diagnostics::PP_MACRO_TOO_MANY_ARGS;
     373           0 :         mDiagnostics->report(id, identifier.location, identifier.text);
     374           0 :         return false;
     375             :     }
     376             : 
     377             :     // Pre-expand each argument before substitution.
     378             :     // This step expands each argument individually before they are
     379             :     // inserted into the macro body.
     380           0 :     size_t numTokens = 0;
     381           0 :     for (auto &arg : *args)
     382             :     {
     383           0 :         TokenLexer lexer(&arg);
     384           0 :         if (mAllowedMacroExpansionDepth < 1)
     385             :         {
     386           0 :             mDiagnostics->report(Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, token.location,
     387           0 :                                  token.text);
     388           0 :             return false;
     389             :         }
     390           0 :         MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mAllowedMacroExpansionDepth - 1);
     391             : 
     392           0 :         arg.clear();
     393           0 :         expander.lex(&token);
     394           0 :         while (token.type != Token::LAST)
     395             :         {
     396           0 :             arg.push_back(token);
     397           0 :             expander.lex(&token);
     398           0 :             numTokens++;
     399           0 :             if (numTokens + mTotalTokensInContexts > kMaxContextTokens)
     400             :             {
     401           0 :                 mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
     402           0 :                 return false;
     403             :             }
     404             :         }
     405             :     }
     406           0 :     return true;
     407             : }
     408             : 
     409           0 : void MacroExpander::replaceMacroParams(const Macro &macro,
     410             :                                        const std::vector<MacroArg> &args,
     411             :                                        std::vector<Token> *replacements)
     412             : {
     413           0 :     for (std::size_t i = 0; i < macro.replacements.size(); ++i)
     414             :     {
     415           0 :         if (!replacements->empty() &&
     416           0 :             replacements->size() + mTotalTokensInContexts > kMaxContextTokens)
     417             :         {
     418           0 :             const Token &token = replacements->back();
     419           0 :             mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
     420           0 :             return;
     421             :         }
     422             : 
     423           0 :         const Token &repl = macro.replacements[i];
     424           0 :         if (repl.type != Token::IDENTIFIER)
     425             :         {
     426           0 :             replacements->push_back(repl);
     427           0 :             continue;
     428             :         }
     429             : 
     430             :         // TODO(alokp): Optimize this.
     431             :         // There is no need to search for macro params every time.
     432             :         // The param index can be cached with the replacement token.
     433             :         Macro::Parameters::const_iterator iter = std::find(
     434           0 :             macro.parameters.begin(), macro.parameters.end(), repl.text);
     435           0 :         if (iter == macro.parameters.end())
     436             :         {
     437           0 :             replacements->push_back(repl);
     438           0 :             continue;
     439             :         }
     440             : 
     441           0 :         std::size_t iArg = std::distance(macro.parameters.begin(), iter);
     442           0 :         const MacroArg &arg = args[iArg];
     443           0 :         if (arg.empty())
     444             :         {
     445           0 :             continue;
     446             :         }
     447           0 :         std::size_t iRepl = replacements->size();
     448           0 :         replacements->insert(replacements->end(), arg.begin(), arg.end());
     449             :         // The replacement token inherits padding properties from
     450             :         // macro replacement token.
     451           0 :         replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
     452             :     }
     453             : }
     454             : 
     455           0 : MacroExpander::MacroContext::MacroContext() : macro(0), index(0)
     456             : {
     457           0 : }
     458             : 
     459           0 : bool MacroExpander::MacroContext::empty() const
     460             : {
     461           0 :     return index == replacements.size();
     462             : }
     463             : 
     464           0 : const Token &MacroExpander::MacroContext::get()
     465             : {
     466           0 :     return replacements[index++];
     467             : }
     468             : 
     469           0 : void MacroExpander::MacroContext::unget()
     470             : {
     471           0 :     ASSERT(index > 0);
     472           0 :     --index;
     473           0 : }
     474             : 
     475           9 : }  // namespace pp
     476             : 

Generated by: LCOV version 1.13