//------------------------------------------------------------------------------ // CLING - the C++ LLVM-based InterpreterG :) // author: Axel Naumann // // 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 "cling/UserInterface/UserInterface.h" #include "cling/UserInterface/CompilationException.h" #include "cling/UserInterface/TabCompletion.h" #include "cling/Interpreter/Exception.h" #include "cling/MetaProcessor/MetaProcessor.h" #include "textinput/TextInput.h" #include "textinput/StreamReader.h" #include "textinput/TerminalDisplay.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" #include "llvm/Config/config.h" #include "clang/Basic/LangOptions.h" #include "clang/Frontend/CompilerInstance.h" // Fragment copied from LLVM's raw_ostream.cpp #if defined(HAVE_UNISTD_H) # include #endif #if defined(LLVM_ON_WIN32) #include #endif #if defined(_MSC_VER) #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO # define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO # define STDERR_FILENO 2 #endif #endif #include namespace { // Handle fatal llvm errors by throwing an exception. // Yes, throwing exceptions in error handlers is bad. // Doing nothing is pretty terrible, too. void exceptionErrorHandler(void * /*user_data*/, const std::string& reason, bool /*gen_crash_diag*/) { throw cling::CompilationException(reason); } #if defined(LLVM_ON_UNIX) static void GetUserHomeDirectory(llvm::SmallVectorImpl& str) { str.clear(); const char* home = getenv("HOME"); if (!home) home = "/"; llvm::StringRef SRhome(home); str.insert(str.begin(), SRhome.begin(), SRhome.end()); } #elif defined(LLVM_ON_WIN32) static void GetUserHomeDirectory(llvm::SmallVectorImpl& str) { str.reserve(MAX_PATH); HRESULT res = SHGetFolderPathA(NULL, CSIDL_FLAG_CREATE | CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, str.data()); if (res != S_OK) { assert(0 && "Failed to get user home directory"); llvm::StringRef SRhome("\\"); str.insert(str.begin(), SRhome.begin(), SRhome.end()); } } #else # error "Unsupported platform." #endif } namespace cling { // Declared in CompilationException.h; vtable pinned here. CompilationException::~CompilationException() throw() {} UserInterface::UserInterface(Interpreter& interp) { // We need stream that doesn't close its file descriptor, thus we are not // using llvm::outs. Keeping file descriptor open we will be able to use // the results in pipes (Savannah #99234). static llvm::raw_fd_ostream m_MPOuts (STDOUT_FILENO, /*ShouldClose*/false); m_MetaProcessor.reset(new MetaProcessor(interp, m_MPOuts)); llvm::install_fatal_error_handler(&exceptionErrorHandler); } UserInterface::~UserInterface() {} void UserInterface::runInteractively(bool nologo /* = false */) { if (!nologo) { PrintLogo(); } llvm::SmallString<512> histfilePath; if (!getenv("CLING_NOHISTORY")) { // History file is $HOME/.cling_history static const char* histfile = ".cling_history"; GetUserHomeDirectory(histfilePath); llvm::sys::path::append(histfilePath, histfile); } using namespace textinput; std::unique_ptr R(StreamReader::Create()); std::unique_ptr D(TerminalDisplay::Create()); TextInput TI(*R, *D, histfilePath.empty() ? 0 : histfilePath.c_str()); // Inform text input about the code complete consumer cling::TabCompletion* Completion = new cling::TabCompletion(m_MetaProcessor->getInterpreter()); TI.SetCompletion(Completion); TI.SetPrompt("[cling]$ "); std::string line; while (true) { try { m_MetaProcessor->getOuts().flush(); TextInput::EReadResult RR = TI.ReadInput(); TI.TakeInput(line); if (RR == TextInput::kRREOF) { break; } cling::Interpreter::CompilationResult compRes; MetaProcessor::MaybeRedirectOutputRAII RAII(m_MetaProcessor.get()); int indent = m_MetaProcessor->process(line.c_str(), compRes, 0/*result*/); // Quit requested if (indent < 0) break; std::string Prompt = "[cling]"; if (m_MetaProcessor->getInterpreter().isRawInputEnabled()) Prompt.append("! "); else Prompt.append("$ "); if (indent > 0) // Continuation requested. Prompt.append('?' + std::string(indent * 3, ' ')); TI.SetPrompt(Prompt.c_str()); } catch(InvalidDerefException& e) { e.diagnose(); } catch(InterpreterException& e) { llvm::errs() << ">>> Caught an interpreter exception!\n" << ">>> " << e.what() << '\n'; } catch(std::exception& e) { llvm::errs() << ">>> Caught a std::exception!\n" << ">>> " << e.what() << '\n'; } catch(...) { llvm::errs() << "Exception occurred. Recovering...\n"; } } } void UserInterface::PrintLogo() { llvm::raw_ostream& outs = m_MetaProcessor->getOuts(); const clang::LangOptions& LangOpts = m_MetaProcessor->getInterpreter().getCI()->getLangOpts(); if (LangOpts.CPlusPlus) { outs << "\n" "****************** CLING ******************\n" "* Type C++ code and press enter to run it *\n" "* Type .q to exit *\n" "*******************************************\n"; } else { outs << "\n" "***************** CLING *****************\n" "* Type C code and press enter to run it *\n" "* Type .q to exit *\n" "*****************************************\n"; } } } // end namespace cling