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> ¯o : 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 ¯o,
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 ¯o,
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 ¶ms = 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 ¯o,
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