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