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