181 lines
5.7 KiB
C++
181 lines
5.7 KiB
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 "cling/UserInterface/UserInterface.h"
|
|
|
|
#include "cling/UserInterface/CompilationException.h"
|
|
#include "cling/Interpreter/RuntimeException.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"
|
|
|
|
// Fragment copied from LLVM's raw_ostream.cpp
|
|
#if defined(HAVE_UNISTD_H)
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(LLVM_ON_WIN32)
|
|
#include <Shlobj.h>
|
|
#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
|
|
|
|
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<char>& 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<char>& 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;
|
|
llvm::OwningPtr<StreamReader> R(StreamReader::Create());
|
|
llvm::OwningPtr<TerminalDisplay> D(TerminalDisplay::Create());
|
|
TextInput TI(*R, *D, histfilePath.empty() ? 0 : histfilePath.c_str());
|
|
|
|
TI.SetPrompt("[cling]$ ");
|
|
std::string line;
|
|
|
|
jmp_buf env;
|
|
int val = setjmp(env);
|
|
if (!val) {
|
|
Interpreter::getNullDerefJump() = &env;
|
|
} else {
|
|
llvm::errs() << "LongJmp occurred. Recovering...\n";
|
|
}
|
|
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(runtime::NullDerefException& e) {
|
|
e.diagnose();
|
|
}
|
|
catch(runtime::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();
|
|
outs << "\n";
|
|
outs << "****************** CLING ******************" << "\n";
|
|
outs << "* Type C++ code and press enter to run it *" << "\n";
|
|
outs << "* Type .q to exit *" << "\n";
|
|
outs << "*******************************************" << "\n";
|
|
}
|
|
} // end namespace cling
|