cling/lib/Interpreter/ClingPragmas.cpp

248 lines
8.1 KiB
C++

//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// author: Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
#include "ClingPragmas.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/Transaction.h"
#include "cling/Utils/Output.h"
#include "cling/Utils/Paths.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/LiteralSupport.h"
#include "clang/Lex/Token.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/ParseDiagnostic.h"
#include <cstdlib>
using namespace cling;
using namespace clang;
namespace {
class ClingPragmaHandler: public PragmaHandler {
Interpreter& m_Interp;
struct SkipToEOD {
Preprocessor& m_PP;
Token& m_Tok;
SkipToEOD(Preprocessor& PParg, Token& Tok):
m_PP(PParg), m_Tok(Tok) {
}
~SkipToEOD() {
// Can't use Preprocessor::DiscardUntilEndOfDirective, as we may
// already be on an eod token
while (!m_Tok.isOneOf(tok::eod, tok::eof))
m_PP.LexUnexpandedToken(m_Tok);
}
};
enum {
kLoadFile,
kAddLibrary,
kAddInclude,
// Put all commands that expand environment variables above this
kExpandEnvCommands,
// Put all commands that only take string literals above this
kArgumentsAreLiterals,
kOptimize,
kInvalidCommand,
};
bool GetNextLiteral(Preprocessor& PP, Token& Tok, std::string& Literal,
unsigned Cmd, const char* firstTime = nullptr) const {
Literal.clear();
PP.Lex(Tok);
if (Tok.isLiteral()) {
if (clang::tok::isStringLiteral(Tok.getKind())) {
SmallVector<Token, 1> StrToks(1, Tok);
StringLiteralParser LitParse(StrToks, PP);
if (!LitParse.hadError)
Literal = LitParse.GetString();
} else {
llvm::SmallString<64> Buffer;
Literal = PP.getSpelling(Tok, Buffer).str();
}
}
else if (Tok.is(tok::comma))
return GetNextLiteral(PP, Tok, Literal, Cmd);
else if (firstTime) {
if (Tok.is(tok::l_paren)) {
if (Cmd < kArgumentsAreLiterals) {
if (!PP.LexStringLiteral(Tok, Literal, firstTime,
false /*allowMacroExpansion*/)) {
// already diagnosed.
return false;
}
} else {
PP.Lex(Tok);
llvm::SmallString<64> Buffer;
Literal = PP.getSpelling(Tok, Buffer).str();
}
}
}
if (Literal.empty())
return false;
if (Cmd < kExpandEnvCommands)
utils::ExpandEnvVars(Literal);
return true;
}
void ReportCommandErr(Preprocessor& PP, const Token& Tok) {
PP.Diag(Tok.getLocation(), diag::err_expected)
<< "load, add_library_path, or add_include_path";
}
int GetCommand(const StringRef CommandStr) {
if (CommandStr == "load")
return kLoadFile;
else if (CommandStr == "add_library_path")
return kAddLibrary;
else if (CommandStr == "add_include_path")
return kAddInclude;
else if (CommandStr == "optimize")
return kOptimize;
return kInvalidCommand;
}
void LoadCommand(Preprocessor& PP, Token& Tok, std::string Literal) {
// No need to load libraries when not executing anything.
if (m_Interp.isInSyntaxOnlyMode())
return;
// Need to parse them all until the end to handle the possible
// #include stements that will be generated
std::vector<std::string> Files;
Files.push_back(std::move(Literal));
while (GetNextLiteral(PP, Tok, Literal, kLoadFile))
Files.push_back(std::move(Literal));
clang::Parser& P = m_Interp.getParser();
Parser::ParserCurTokRestoreRAII savedCurToken(P);
// After we have saved the token reset the current one to something
// which is safe (semi colon usually means empty decl)
Token& CurTok = const_cast<Token&>(P.getCurToken());
CurTok.setKind(tok::semi);
Preprocessor::CleanupAndRestoreCacheRAII cleanupRAII(PP);
// We can't PushDeclContext, because we go up and the routine that
// pops the DeclContext assumes that we drill down always.
// We have to be on the global context. At that point we are in a
// wrapper function so the parent context must be the global.
TranslationUnitDecl* TU =
m_Interp.getCI()->getASTContext().getTranslationUnitDecl();
Sema::ContextAndScopeRAII pushedDCAndS(m_Interp.getSema(),
TU, m_Interp.getSema().TUScope);
Interpreter::PushTransactionRAII pushedT(&m_Interp);
for (std::string& File : Files) {
if (m_Interp.loadFile(File, true) != Interpreter::kSuccess)
return;
}
}
void OptimizeCommand(const char* Str) {
char* ConvEnd = nullptr;
int OptLevel = std::strtol(Str, &ConvEnd, 10 /*base*/);
if (!ConvEnd || ConvEnd == Str) {
cling::errs() << "cling::PHOptLevel: "
"missing or non-numerical optimization level.\n" ;
return;
}
auto T = const_cast<Transaction*>(m_Interp.getCurrentTransaction());
assert(T && "Parsing code without transaction!");
// The topmost Transaction drives the jitting.
T = T->getTopmostParent();
CompilationOptions& CO = T->getCompilationOpts();
if (CO.OptLevel != m_Interp.getDefaultOptLevel()) {
// Another #pragma already changed the opt level, a conflict that
// cannot be resolve here. Mention and keep the lower one.
cling::errs() << "cling::PHOptLevel: "
"conflicting `#pragma cling optimize` directives: "
"was already set to " << CO.OptLevel << '\n';
if (CO.OptLevel > OptLevel) {
CO.OptLevel = OptLevel;
cling::errs() << "Setting to lower value of " << OptLevel << '\n';
} else {
cling::errs() << "Ignoring higher value of " << OptLevel << '\n';
}
} else
CO.OptLevel = OptLevel;
}
public:
ClingPragmaHandler(Interpreter& interp):
PragmaHandler("cling"), m_Interp(interp) {}
void HandlePragma(Preprocessor& PP,
PragmaIntroducerKind Introducer,
Token& FirstToken) override {
Token Tok;
PP.Lex(Tok);
SkipToEOD OnExit(PP, Tok);
// #pragma cling(load, "A")
if (Tok.is(tok::l_paren))
PP.Lex(Tok);
if (Tok.isNot(tok::identifier)) {
ReportCommandErr(PP, Tok);
return;
}
const StringRef CommandStr = Tok.getIdentifierInfo()->getName();
const unsigned Command = GetCommand(CommandStr);
assert(Command != kArgumentsAreLiterals && Command != kExpandEnvCommands);
if (Command == kInvalidCommand) {
ReportCommandErr(PP, Tok);
return;
}
std::string Literal;
if (!GetNextLiteral(PP, Tok, Literal, Command, CommandStr.data())) {
PP.Diag(Tok.getLocation(), diag::err_expected_after)
<< CommandStr << "argument";
return;
}
switch (Command) {
case kLoadFile:
return LoadCommand(PP, Tok, std::move(Literal));
case kOptimize:
return OptimizeCommand(Literal.c_str());
default:
do {
if (Command == kAddLibrary)
m_Interp.getOptions().LibSearchPath.push_back(std::move(Literal));
else if (Command == kAddInclude)
m_Interp.AddIncludePath(Literal);
} while (GetNextLiteral(PP, Tok, Literal, Command));
break;
}
}
};
}
void cling::addClingPragmas(Interpreter& interp) {
Preprocessor& PP = interp.getCI()->getPreprocessor();
// PragmaNamespace / PP takes ownership of sub-handlers.
PP.AddPragmaHandler(StringRef(), new ClingPragmaHandler(interp));
}