#include "MacroArgs.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/LexDiagnostic.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/SaveAndRestore.h"
#include <algorithm>
using namespace clang;
MacroArgs *MacroArgs::create(const MacroInfo *MI,
llvm::ArrayRef<Token> UnexpArgTokens,
bool VarargsElided, Preprocessor &PP) {
assert(MI->isFunctionLike() &&
"Can't have args for an object-like macro!");
MacroArgs **ResultEnt = 0;
unsigned ClosestMatch = ~0U;
for (MacroArgs **Entry = &PP.MacroArgCache; *Entry;
Entry = &(*Entry)->ArgCache)
if ((*Entry)->NumUnexpArgTokens >= UnexpArgTokens.size() &&
(*Entry)->NumUnexpArgTokens < ClosestMatch) {
ResultEnt = Entry;
if ((*Entry)->NumUnexpArgTokens == UnexpArgTokens.size())
break;
ClosestMatch = (*Entry)->NumUnexpArgTokens;
}
MacroArgs *Result;
if (ResultEnt == 0) {
Result = (MacroArgs*)malloc(sizeof(MacroArgs) +
UnexpArgTokens.size() * sizeof(Token));
new (Result) MacroArgs(UnexpArgTokens.size(), VarargsElided);
} else {
Result = *ResultEnt;
*ResultEnt = Result->ArgCache;
Result->NumUnexpArgTokens = UnexpArgTokens.size();
Result->VarargsElided = VarargsElided;
}
if (!UnexpArgTokens.empty())
std::copy(UnexpArgTokens.begin(), UnexpArgTokens.end(),
const_cast<Token*>(Result->getUnexpArgument(0)));
return Result;
}
void MacroArgs::destroy(Preprocessor &PP) {
StringifiedArgs.clear();
for (unsigned i = 0, e = PreExpArgTokens.size(); i != e; ++i)
PreExpArgTokens[i].clear();
ArgCache = PP.MacroArgCache;
PP.MacroArgCache = this;
}
MacroArgs *MacroArgs::deallocate() {
MacroArgs *Next = ArgCache;
this->~MacroArgs();
free(this);
return Next;
}
unsigned MacroArgs::getArgLength(const Token *ArgPtr) {
unsigned NumArgTokens = 0;
for (; ArgPtr->isNot(tok::eof); ++ArgPtr)
++NumArgTokens;
return NumArgTokens;
}
const Token *MacroArgs::getUnexpArgument(unsigned Arg) const {
const Token *Start = (const Token *)(this+1);
const Token *Result = Start;
for (; Arg; ++Result) {
assert(Result < Start+NumUnexpArgTokens && "Invalid arg #");
if (Result->is(tok::eof))
--Arg;
}
assert(Result < Start+NumUnexpArgTokens && "Invalid arg #");
return Result;
}
bool MacroArgs::ArgNeedsPreexpansion(const Token *ArgTok,
Preprocessor &PP) const {
for (; ArgTok->isNot(tok::eof); ++ArgTok)
if (IdentifierInfo *II = ArgTok->getIdentifierInfo()) {
if (II->hasMacroDefinition() && PP.getMacroInfo(II)->isEnabled())
return true;
}
return false;
}
const std::vector<Token> &
MacroArgs::getPreExpArgument(unsigned Arg, const MacroInfo *MI,
Preprocessor &PP) {
assert(Arg < MI->getNumArgs() && "Invalid argument number!");
if (PreExpArgTokens.size() < MI->getNumArgs())
PreExpArgTokens.resize(MI->getNumArgs());
std::vector<Token> &Result = PreExpArgTokens[Arg];
if (!Result.empty()) return Result;
SaveAndRestore<bool> PreExpandingMacroArgs(PP.InMacroArgPreExpansion, true);
const Token *AT = getUnexpArgument(Arg);
unsigned NumToks = getArgLength(AT)+1;
PP.EnterTokenStream(AT, NumToks, false ,
false );
do {
Result.push_back(Token());
Token &Tok = Result.back();
PP.Lex(Tok);
} while (Result.back().isNot(tok::eof));
if (PP.InCachingLexMode())
PP.ExitCachingLexMode();
PP.RemoveTopOfLexerStack();
return Result;
}
Token MacroArgs::StringifyArgument(const Token *ArgToks,
Preprocessor &PP, bool Charify,
SourceLocation ExpansionLocStart,
SourceLocation ExpansionLocEnd) {
Token Tok;
Tok.startToken();
Tok.setKind(Charify ? tok::char_constant : tok::string_literal);
const Token *ArgTokStart = ArgToks;
SmallString<128> Result;
Result += "\"";
bool isFirst = true;
for (; ArgToks->isNot(tok::eof); ++ArgToks) {
const Token &Tok = *ArgToks;
if (!isFirst && (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()))
Result += ' ';
isFirst = false;
if (Tok.is(tok::string_literal) || Tok.is(tok::wide_string_literal) || Tok.is(tok::utf8_string_literal) || Tok.is(tok::utf16_string_literal) || Tok.is(tok::utf32_string_literal) || Tok.is(tok::char_constant) || Tok.is(tok::wide_char_constant) || Tok.is(tok::utf16_char_constant) || Tok.is(tok::utf32_char_constant)) { bool Invalid = false;
std::string TokStr = PP.getSpelling(Tok, &Invalid);
if (!Invalid) {
std::string Str = Lexer::Stringify(TokStr);
Result.append(Str.begin(), Str.end());
}
} else if (Tok.is(tok::code_completion)) {
PP.CodeCompleteNaturalLanguage();
} else {
unsigned CurStrLen = Result.size();
Result.resize(CurStrLen+Tok.getLength());
const char *BufPtr = &Result[CurStrLen];
bool Invalid = false;
unsigned ActualTokLen = PP.getSpelling(Tok, BufPtr, &Invalid);
if (!Invalid) {
if (BufPtr != &Result[CurStrLen])
memcpy(&Result[CurStrLen], BufPtr, ActualTokLen);
if (ActualTokLen != Tok.getLength())
Result.resize(CurStrLen+ActualTokLen);
}
}
}
if (Result.back() == '\\') {
unsigned FirstNonSlash = Result.size()-2;
while (Result[FirstNonSlash] == '\\')
--FirstNonSlash;
if ((Result.size()-1-FirstNonSlash) & 1) {
PP.Diag(ArgToks[-1], diag::pp_invalid_string_literal);
Result.pop_back(); }
}
Result += '"';
if (Charify) {
Result[0] = '\'';
Result[Result.size()-1] = '\'';
bool isBad = false;
if (Result.size() == 3)
isBad = Result[1] == '\''; else
isBad = (Result.size() != 4 || Result[1] != '\\');
if (isBad) {
PP.Diag(ArgTokStart[0], diag::err_invalid_character_to_charify);
Result = "' '"; }
}
PP.CreateString(Result, Tok,
ExpansionLocStart, ExpansionLocEnd);
return Tok;
}
const Token &MacroArgs::getStringifiedArgument(unsigned ArgNo,
Preprocessor &PP,
SourceLocation ExpansionLocStart,
SourceLocation ExpansionLocEnd) {
assert(ArgNo < NumUnexpArgTokens && "Invalid argument number!");
if (StringifiedArgs.empty()) {
StringifiedArgs.resize(getNumArguments());
memset((void*)&StringifiedArgs[0], 0,
sizeof(StringifiedArgs[0])*getNumArguments());
}
if (StringifiedArgs[ArgNo].isNot(tok::string_literal))
StringifiedArgs[ArgNo] = StringifyArgument(getUnexpArgument(ArgNo), PP,
false,
ExpansionLocStart,
ExpansionLocEnd);
return StringifiedArgs[ArgNo];
}