Path: blob/main_old/src/compiler/preprocessor/MacroExpander.cpp
1693 views
//1// Copyright 2011 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//56#include "compiler/preprocessor/MacroExpander.h"78#include <GLSLANG/ShaderLang.h>9#include <algorithm>1011#include "common/debug.h"12#include "compiler/preprocessor/DiagnosticsBase.h"13#include "compiler/preprocessor/Token.h"1415namespace angle16{1718namespace pp19{2021namespace22{2324const size_t kMaxContextTokens = 10000;2526class TokenLexer : public Lexer27{28public:29typedef std::vector<Token> TokenVector;3031TokenLexer(TokenVector *tokens)32{33tokens->swap(mTokens);34mIter = mTokens.begin();35}3637void lex(Token *token) override38{39if (mIter == mTokens.end())40{41token->reset();42token->type = Token::LAST;43}44else45{46*token = *mIter++;47}48}4950private:51TokenVector mTokens;52TokenVector::const_iterator mIter;53};5455} // anonymous namespace5657class MacroExpander::ScopedMacroReenabler final : angle::NonCopyable58{59public:60ScopedMacroReenabler(MacroExpander *expander);61~ScopedMacroReenabler();6263private:64MacroExpander *mExpander;65};6667MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expander)68: mExpander(expander)69{70mExpander->mDeferReenablingMacros = true;71}7273MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()74{75mExpander->mDeferReenablingMacros = false;76for (const std::shared_ptr<Macro> ¯o : mExpander->mMacrosToReenable)77{78// Copying the string here by using substr is a check for use-after-free. It detects79// use-after-free more reliably than just toggling the disabled flag.80ASSERT(macro->name.substr() != "");81macro->disabled = false;82}83mExpander->mMacrosToReenable.clear();84}8586MacroExpander::MacroExpander(Lexer *lexer,87MacroSet *macroSet,88Diagnostics *diagnostics,89const PreprocessorSettings &settings,90bool parseDefined)91: mLexer(lexer),92mMacroSet(macroSet),93mDiagnostics(diagnostics),94mParseDefined(parseDefined),95mTotalTokensInContexts(0),96mSettings(settings),97mDeferReenablingMacros(false)98{}99100MacroExpander::~MacroExpander()101{102ASSERT(mMacrosToReenable.empty());103for (MacroContext *context : mContextStack)104{105delete context;106}107}108109void MacroExpander::lex(Token *token)110{111while (true)112{113getToken(token);114115if (token->type != Token::IDENTIFIER)116break;117118// Defined operator is parsed here since it may be generated by macro expansion.119// Defined operator produced by macro expansion has undefined behavior according to C++120// spec, which the GLSL spec references (see C++14 draft spec section 16.1.4), but this121// behavior is needed for passing dEQP tests, which enforce stricter compatibility between122// implementations.123if (mParseDefined && token->text == kDefined)124{125// Defined inside a macro is forbidden in WebGL.126if (!mContextStack.empty() && sh::IsWebGLBasedSpec(mSettings.shaderSpec))127break;128129bool paren = false;130getToken(token);131if (token->type == '(')132{133paren = true;134getToken(token);135}136if (token->type != Token::IDENTIFIER)137{138mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,139token->text);140break;141}142auto iter = mMacroSet->find(token->text);143std::string expression = iter != mMacroSet->end() ? "1" : "0";144145if (paren)146{147getToken(token);148if (token->type != ')')149{150mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,151token->text);152break;153}154}155156// We have a valid defined operator.157// Convert the current token into a CONST_INT token.158token->type = Token::CONST_INT;159token->text = expression;160break;161}162163if (token->expansionDisabled())164break;165166MacroSet::const_iterator iter = mMacroSet->find(token->text);167if (iter == mMacroSet->end())168break;169170std::shared_ptr<Macro> macro = iter->second;171if (macro->disabled)172{173// If a particular token is not expanded, it is never expanded.174token->setExpansionDisabled(true);175break;176}177178// Bump the expansion count before peeking if the next token is a '('179// otherwise there could be a #undef of the macro before the next token.180macro->expansionCount++;181if ((macro->type == Macro::kTypeFunc) && !isNextTokenLeftParen())182{183// If the token immediately after the macro name is not a '(',184// this macro should not be expanded.185macro->expansionCount--;186break;187}188189pushMacro(macro, *token);190}191}192193void MacroExpander::getToken(Token *token)194{195if (mReserveToken.get())196{197*token = *mReserveToken;198mReserveToken.reset();199return;200}201202// First pop all empty macro contexts.203while (!mContextStack.empty() && mContextStack.back()->empty())204{205popMacro();206}207208if (!mContextStack.empty())209{210*token = mContextStack.back()->get();211}212else213{214ASSERT(mTotalTokensInContexts == 0);215mLexer->lex(token);216}217}218219void MacroExpander::ungetToken(const Token &token)220{221if (!mContextStack.empty())222{223MacroContext *context = mContextStack.back();224context->unget();225ASSERT(context->replacements[context->index] == token);226}227else228{229ASSERT(!mReserveToken.get());230mReserveToken.reset(new Token(token));231}232}233234bool MacroExpander::isNextTokenLeftParen()235{236Token token;237getToken(&token);238239bool lparen = token.type == '(';240ungetToken(token);241242return lparen;243}244245bool MacroExpander::pushMacro(std::shared_ptr<Macro> macro, const Token &identifier)246{247ASSERT(!macro->disabled);248ASSERT(!identifier.expansionDisabled());249ASSERT(identifier.type == Token::IDENTIFIER);250ASSERT(identifier.text == macro->name);251252std::vector<Token> replacements;253if (!expandMacro(*macro, identifier, &replacements))254return false;255256// Macro is disabled for expansion until it is popped off the stack.257macro->disabled = true;258259MacroContext *context = new MacroContext;260context->macro = macro;261context->replacements.swap(replacements);262mContextStack.push_back(context);263mTotalTokensInContexts += context->replacements.size();264return true;265}266267void MacroExpander::popMacro()268{269ASSERT(!mContextStack.empty());270271MacroContext *context = mContextStack.back();272mContextStack.pop_back();273274ASSERT(context->empty());275ASSERT(context->macro->disabled);276ASSERT(context->macro->expansionCount > 0);277if (mDeferReenablingMacros)278{279mMacrosToReenable.push_back(context->macro);280}281else282{283context->macro->disabled = false;284}285context->macro->expansionCount--;286mTotalTokensInContexts -= context->replacements.size();287delete context;288}289290bool MacroExpander::expandMacro(const Macro ¯o,291const Token &identifier,292std::vector<Token> *replacements)293{294replacements->clear();295296// In the case of an object-like macro, the replacement list gets its location297// from the identifier, but in the case of a function-like macro, the replacement298// list gets its location from the closing parenthesis of the macro invocation.299// This is tested by dEQP-GLES3.functional.shaders.preprocessor.predefined_macros.*300SourceLocation replacementLocation = identifier.location;301if (macro.type == Macro::kTypeObj)302{303replacements->assign(macro.replacements.begin(), macro.replacements.end());304305if (macro.predefined)306{307const char kLine[] = "__LINE__";308const char kFile[] = "__FILE__";309310ASSERT(replacements->size() == 1);311Token &repl = replacements->front();312if (macro.name == kLine)313{314repl.text = ToString(identifier.location.line);315}316else if (macro.name == kFile)317{318repl.text = ToString(identifier.location.file);319}320}321}322else323{324ASSERT(macro.type == Macro::kTypeFunc);325std::vector<MacroArg> args;326args.reserve(macro.parameters.size());327if (!collectMacroArgs(macro, identifier, &args, &replacementLocation))328return false;329330replaceMacroParams(macro, args, replacements);331}332333for (std::size_t i = 0; i < replacements->size(); ++i)334{335Token &repl = replacements->at(i);336if (i == 0)337{338// The first token in the replacement list inherits the padding339// properties of the identifier token.340repl.setAtStartOfLine(identifier.atStartOfLine());341repl.setHasLeadingSpace(identifier.hasLeadingSpace());342}343repl.location = replacementLocation;344}345return true;346}347348bool MacroExpander::collectMacroArgs(const Macro ¯o,349const Token &identifier,350std::vector<MacroArg> *args,351SourceLocation *closingParenthesisLocation)352{353Token token;354getToken(&token);355ASSERT(token.type == '(');356357args->push_back(MacroArg());358359// Defer reenabling macros until args collection is finished to avoid the possibility of360// infinite recursion. Otherwise infinite recursion might happen when expanding the args after361// macros have been popped from the context stack when parsing the args.362ScopedMacroReenabler deferReenablingMacros(this);363364int openParens = 1;365while (openParens != 0)366{367getToken(&token);368369if (token.type == Token::LAST)370{371mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION, identifier.location,372identifier.text);373// Do not lose EOF token.374ungetToken(token);375return false;376}377378bool isArg = false; // True if token is part of the current argument.379switch (token.type)380{381case '(':382++openParens;383isArg = true;384break;385case ')':386--openParens;387isArg = openParens != 0;388*closingParenthesisLocation = token.location;389break;390case ',':391// The individual arguments are separated by comma tokens, but392// the comma tokens between matching inner parentheses do not393// seperate arguments.394if (openParens == 1)395args->push_back(MacroArg());396isArg = openParens != 1;397break;398default:399isArg = true;400break;401}402if (isArg)403{404MacroArg &arg = args->back();405// Initial whitespace is not part of the argument.406if (arg.empty())407token.setHasLeadingSpace(false);408arg.push_back(token);409}410}411412const Macro::Parameters ¶ms = macro.parameters;413// If there is only one empty argument, it is equivalent to no argument.414if (params.empty() && (args->size() == 1) && args->front().empty())415{416args->clear();417}418// Validate the number of arguments.419if (args->size() != params.size())420{421Diagnostics::ID id = args->size() < macro.parameters.size()422? Diagnostics::PP_MACRO_TOO_FEW_ARGS423: Diagnostics::PP_MACRO_TOO_MANY_ARGS;424mDiagnostics->report(id, identifier.location, identifier.text);425return false;426}427428// Pre-expand each argument before substitution.429// This step expands each argument individually before they are430// inserted into the macro body.431size_t numTokens = 0;432for (auto &arg : *args)433{434TokenLexer lexer(&arg);435if (mSettings.maxMacroExpansionDepth < 1)436{437mDiagnostics->report(Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, token.location,438token.text);439return false;440}441PreprocessorSettings nestedSettings(mSettings.shaderSpec);442nestedSettings.maxMacroExpansionDepth = mSettings.maxMacroExpansionDepth - 1;443MacroExpander expander(&lexer, mMacroSet, mDiagnostics, nestedSettings, mParseDefined);444445arg.clear();446expander.lex(&token);447while (token.type != Token::LAST)448{449arg.push_back(token);450expander.lex(&token);451numTokens++;452if (numTokens + mTotalTokensInContexts > kMaxContextTokens)453{454mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);455return false;456}457}458}459return true;460}461462void MacroExpander::replaceMacroParams(const Macro ¯o,463const std::vector<MacroArg> &args,464std::vector<Token> *replacements)465{466for (std::size_t i = 0; i < macro.replacements.size(); ++i)467{468if (!replacements->empty() &&469replacements->size() + mTotalTokensInContexts > kMaxContextTokens)470{471const Token &token = replacements->back();472mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);473return;474}475476const Token &repl = macro.replacements[i];477if (repl.type != Token::IDENTIFIER)478{479replacements->push_back(repl);480continue;481}482483// TODO(alokp): Optimize this.484// There is no need to search for macro params every time.485// The param index can be cached with the replacement token.486Macro::Parameters::const_iterator iter =487std::find(macro.parameters.begin(), macro.parameters.end(), repl.text);488if (iter == macro.parameters.end())489{490replacements->push_back(repl);491continue;492}493494std::size_t iArg = std::distance(macro.parameters.begin(), iter);495const MacroArg &arg = args[iArg];496if (arg.empty())497{498continue;499}500std::size_t iRepl = replacements->size();501replacements->insert(replacements->end(), arg.begin(), arg.end());502// The replacement token inherits padding properties from503// macro replacement token.504replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());505}506}507508MacroExpander::MacroContext::MacroContext() : macro(0), index(0) {}509510MacroExpander::MacroContext::~MacroContext() {}511512bool MacroExpander::MacroContext::empty() const513{514return index == replacements.size();515}516517const Token &MacroExpander::MacroContext::get()518{519return replacements[index++];520}521522void MacroExpander::MacroContext::unget()523{524ASSERT(index > 0);525--index;526}527528} // namespace pp529530} // namespace angle531532533