├── Utils.h ├── CMakeLists.txt ├── CCIntJIT.h ├── CCIntParser.h ├── Interpreter.h ├── Driver.cpp ├── README.md ├── Utils.cpp ├── CCIntJIT.cpp ├── CCIntParser.cpp ├── doc └── ccint.svg ├── Interpreter.cpp └── LICENSE /Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVM_CLANG_TOOLS_CLANG_CCINT_UTILS_H 2 | #define LLVM_CLANG_TOOLS_CLANG_CCINT_UTILS_H 3 | 4 | #include "llvm/ADT/StringRef.h" 5 | 6 | #include 7 | 8 | namespace clang { 9 | class LangOptions; 10 | class FunctionDecl; 11 | 12 | size_t getWrapPos(const clang::LangOptions &LangOpts, const std::string &Code); 13 | bool isCCIntMain(clang::FunctionDecl *FD); 14 | 15 | bool isDynamicLibrary(llvm::StringRef Path); 16 | 17 | } // namespace clang 18 | 19 | #endif // LLVM_CLANG_TOOLS_CLANG_CCINT_UTILS_H -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set( LLVM_LINK_COMPONENTS 2 | ${LLVM_TARGETS_TO_BUILD} 3 | Core 4 | LineEditor 5 | Option 6 | OrcJIT 7 | Support 8 | native 9 | Target 10 | ) 11 | 12 | 13 | add_clang_tool(clang-ccint 14 | Driver.cpp 15 | CCIntJIT.cpp 16 | Interpreter.cpp 17 | CCIntParser.cpp 18 | Utils.cpp 19 | ) 20 | 21 | clang_target_link_libraries(clang-ccint PRIVATE 22 | clangBasic 23 | clangFrontend 24 | clangTooling 25 | clangAST 26 | clangAnalysis 27 | clangDriver 28 | clangEdit 29 | clangLex 30 | clangParse 31 | clangSema 32 | clangSerialization 33 | clangCodeGen 34 | clangFrontendTool 35 | ) -------------------------------------------------------------------------------- /CCIntJIT.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVM_CLANG_TOOLS_CLANG_CCINT_CCINT_JIT_H 2 | #define LLVM_CLANG_TOOLS_CLANG_CCINT_CCINT_JIT_H 3 | 4 | #include "llvm/ADT/DenseMap.h" 5 | #include "llvm/ADT/StringRef.h" 6 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 7 | #include 8 | 9 | namespace llvm { 10 | class Error; 11 | namespace orc { 12 | class LLJIT; 13 | class ThreadSafeContext; 14 | } // namespace orc 15 | } // namespace llvm 16 | 17 | namespace clang { 18 | 19 | class TargetInfo; 20 | 21 | class CCIntJIT { 22 | std::unique_ptr Jit; 23 | llvm::orc::ThreadSafeContext &TSCtx; 24 | 25 | llvm::DenseMap 26 | ResourceTrackers; 27 | 28 | public: 29 | CCIntJIT(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err, 30 | const clang::TargetInfo &TI); 31 | ~CCIntJIT(); 32 | 33 | llvm::Error addModule(std::unique_ptr TheModule); 34 | llvm::Error removeModule(std::unique_ptr TheModule); 35 | llvm::Error runCtors() const; 36 | llvm::Expected 37 | getSymbolAddress(llvm::StringRef Name) const; 38 | llvm::Error AddStaticLib(llvm::StringRef Path); 39 | llvm::Error AddDynamicLib(llvm::StringRef Path); 40 | }; 41 | 42 | } // end namespace clang 43 | 44 | #endif // LLVM_CLANG_TOOLS_CLANG_CCINT_CCINT_JIT_H 45 | -------------------------------------------------------------------------------- /CCIntParser.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVM_CLANG_TOOLS_CLANG_CCINT_CCINT_PARSER_H 2 | #define LLVM_CLANG_TOOLS_CLANG_CCINT_CCINT_PARSER_H 3 | 4 | #include "clang/AST/GlobalDecl.h" 5 | #include "clang/CodeGen/CodeGenAction.h" 6 | #include "llvm/ADT/ArrayRef.h" 7 | #include "llvm/ADT/StringRef.h" 8 | #include "llvm/Support/Error.h" 9 | 10 | #include 11 | #include 12 | namespace llvm { 13 | class LLVMContext; 14 | class Module; 15 | } // namespace llvm 16 | 17 | namespace clang { 18 | class ASTConsumer; 19 | class CompilerInstance; 20 | class CCIntAction; 21 | class Parser; 22 | 23 | class CCIntParser { 24 | std::unique_ptr Act; 25 | std::shared_ptr CI; 26 | std::shared_ptr P; 27 | llvm::StringRef MangledName; 28 | std::unique_ptr TheModule; 29 | 30 | public: 31 | CCIntParser(std::unique_ptr Instance, 32 | llvm::LLVMContext &LLVMCtx, llvm::Error &Err); 33 | ~CCIntParser(); 34 | 35 | CompilerInstance *getCI() { return CI.get(); } 36 | std::unique_ptr getModule() { return std::move(TheModule); } 37 | 38 | llvm::Error Parse(llvm::StringRef FileName, bool Wrap); 39 | 40 | llvm::StringRef GetMangledName() const; 41 | std::string WrapInput(const std::string &Code); 42 | }; 43 | } // end namespace clang 44 | 45 | #endif // LLVM_CLANG_TOOLS_CLANG_CCINT_CCINT_PARSER_H 46 | -------------------------------------------------------------------------------- /Interpreter.h: -------------------------------------------------------------------------------- 1 | #ifndef LLVM_CLANG_TOOLS_CLANG_CCINT_INTERPRETER_H 2 | #define LLVM_CLANG_TOOLS_CLANG_CCINT_INTERPRETER_H 3 | 4 | #include "clang/AST/GlobalDecl.h" 5 | 6 | #include "llvm/ExecutionEngine/JITSymbol.h" 7 | #include "llvm/Support/Error.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace llvm { 13 | class Module; 14 | 15 | namespace orc { 16 | class LLJIT; 17 | class ThreadSafeContext; 18 | } // namespace orc 19 | } // namespace llvm 20 | 21 | namespace clang { 22 | 23 | class CompilerInstance; 24 | class CCIntJIT; 25 | class CCIntParser; 26 | 27 | class Interpreter { 28 | bool m_WrapInput; 29 | std::vector StaticLibVec; 30 | std::vector DynamicLibVec; 31 | std::vector HeaderPathVec; 32 | 33 | std::unique_ptr TSCtx; 34 | std::unique_ptr Parser; 35 | std::unique_ptr Executor; 36 | Interpreter(std::unique_ptr CI, llvm::Error &Err); 37 | 38 | public: 39 | ~Interpreter(); 40 | static llvm::Expected> CreateCI(); 41 | static llvm::Expected> 42 | create(std::unique_ptr CI); 43 | CompilerInstance *getCompilerInstance(); 44 | std::unique_ptr getModule(); 45 | 46 | void AddIncludePath(llvm::StringRef Path); 47 | void PrintIncludePath(); 48 | 49 | void AddStaticLib(llvm::StringRef Path); 50 | void AddDynamicLib(llvm::StringRef Path); 51 | void AddHeaderPath(llvm::StringRef Path); 52 | 53 | llvm::Error Parse(llvm::StringRef FileName); 54 | 55 | llvm::Error Execute(); 56 | 57 | llvm::Error ParseAndExecute(llvm::StringRef FileName) { 58 | if (auto Err = Parse(FileName)) { 59 | return Err; 60 | } 61 | return Execute(); 62 | } 63 | 64 | bool isWrapInputEnabled() const { return m_WrapInput; } 65 | void enablerWrapInput(bool wrap = true) { m_WrapInput = wrap; } 66 | 67 | llvm::Expected getSymbolAddress() const; 68 | }; 69 | } // namespace clang 70 | 71 | #endif // LLVM_CLANG_TOOLS_CLANG_CCINT_INTERPRETER_H 72 | -------------------------------------------------------------------------------- /Driver.cpp: -------------------------------------------------------------------------------- 1 | #include "Interpreter.h" 2 | #include "Utils.h" 3 | #include "clang/Basic/Diagnostic.h" 4 | #include "clang/Frontend/CompilerInstance.h" 5 | #include "clang/Frontend/FrontendDiagnostic.h" 6 | 7 | #include "llvm/ExecutionEngine/Orc/LLJIT.h" 8 | #include "llvm/LineEditor/LineEditor.h" 9 | #include "llvm/Support/CommandLine.h" 10 | #include "llvm/Support/ManagedStatic.h" // llvm_shutdown 11 | #include "llvm/Support/Signals.h" 12 | #include "llvm/Support/TargetSelect.h" // llvm::Initialize* 13 | 14 | static void LLVMErrorHandler(void *UserData, const char *Message, 15 | bool GenCrashDiag) { 16 | auto &Diags = *static_cast(UserData); 17 | Diags.Report(clang::diag::err_fe_error_backend) << Message; 18 | llvm::sys::RunInterruptHandlers(); 19 | exit(GenCrashDiag ? 70 : 1); 20 | } 21 | 22 | llvm::ExitOnError ExitOnErr; 23 | 24 | static llvm::cl::opt 25 | wrap("w", llvm::cl::desc("wrap statement to declaration")); 26 | 27 | static llvm::cl::list 28 | IncludePaths("I", llvm::cl::desc("specify include paths"), 29 | llvm::cl::ZeroOrMore); 30 | 31 | static llvm::cl::list Libs("L", llvm::cl::desc("load given libs"), 32 | llvm::cl::ZeroOrMore); 33 | 34 | static llvm::cl::opt inputFile(llvm::cl::Positional, 35 | llvm::cl::desc(""), 36 | llvm::cl::Required); 37 | 38 | int main(int argc, const char **argv) { 39 | llvm::cl::ParseCommandLineOptions(argc, argv); 40 | 41 | auto CI = ExitOnErr(clang::Interpreter::CreateCI()); 42 | 43 | llvm::install_fatal_error_handler(LLVMErrorHandler, 44 | static_cast(&CI->getDiagnostics())); 45 | // llvm::report_fatal_error("test:"); 46 | 47 | auto Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); 48 | 49 | Interp->enablerWrapInput(wrap); 50 | 51 | Interp->AddIncludePath("."); 52 | for (size_t i = 0; i < IncludePaths.size(); i++) { 53 | Interp->AddIncludePath(IncludePaths[i]); 54 | } 55 | 56 | for (size_t i = 0; i < Libs.size(); i++) { 57 | if (clang::isDynamicLibrary(Libs[i])) { 58 | Interp->AddDynamicLib(Libs[i]); 59 | } else { 60 | Interp->AddStaticLib(Libs[i]); 61 | } 62 | } 63 | 64 | if (auto Err = Interp->ParseAndExecute(inputFile)) { 65 | llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); 66 | return 0; 67 | } 68 | 69 | llvm::remove_fatal_error_handler(); 70 | llvm::llvm_shutdown(); 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ccint: a C/C++ interpreter 2 | === 3 | 4 | ccint is a C/C++ interpreter, built on top of Clang and LLVM compiler infrastructure. 5 | 6 | ccint internals 7 | 8 | ## features 9 | 10 | * supports all C++11/C++14/C++17 features 11 | * supports for linking static/dynamic libraries 12 | * high performance: same performance as C/C++ binaries 13 | * separate parser and execution engine 14 | 15 | ## build 16 | 17 | ``` 18 | $ git clone git@github.com:llvm/llvm-project.git 19 | $ cd llvm-project/clang/tools/ 20 | $ git clone git@github.com:remysys/ccint.git 21 | $ echo "add_clang_subdirectory(ccint)" >> CMakeLists.txt 22 | $ cd ../.. 23 | $ cmake -S llvm -B build -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" -DCMAKE_BUILD_TYPE=Release 24 | $ cd build && make -j16 25 | ``` 26 | 27 | ## usage 28 | 29 | ``` 30 | ./clang-ccint --help 31 | USAGE: clang-ccint [options] 32 | 33 | OPTIONS: 34 | General options: 35 | -I - specify include paths 36 | -L - load given libs 37 | ``` 38 | ### examples 39 | 40 | * print "hello world" 41 | ``` 42 | /* main.cpp */ 43 | #include 44 | #include 45 | #include 46 | 47 | void ccint_main() { 48 | std::vector vec = {"hello", " world\n"}; 49 | for (auto &s : vec) { 50 | printf("%s", s.c_str()); 51 | } 52 | } 53 | 54 | $ ./ccint main.cpp 55 | hello world 56 | ``` 57 | 58 | * link static library 59 | ``` 60 | /* add.h */ 61 | int add(int, int); 62 | 63 | /* add.c */ 64 | int add(int a, int b) { 65 | return a + b; 66 | } 67 | 68 | /* main.cpp */ 69 | #include 70 | #include "add.h" 71 | 72 | void ccint_main() { 73 | printf("32 + 64 = %d\n", add(32, 64)); 74 | } 75 | 76 | $ clang -c add.c -o add.o 77 | $ ar rcs libadd.a add.o 78 | $ ./ccint main.cpp -L ./libadd.a 79 | 32 + 64 = 96 80 | ``` 81 | 82 | * link dynamic library 83 | 84 | ``` 85 | /* add.h */ 86 | int add(int, int); 87 | 88 | /* add.c */ 89 | 90 | int add(int a, int b) { 91 | return a + b; 92 | } 93 | 94 | /* main.cpp */ 95 | 96 | #include 97 | #include "add.h" 98 | 99 | void ccint_main() { 100 | printf("32 + 64 = %d\n", add(32, 64)); 101 | } 102 | 103 | $ clang -shared -fPIC add.c -o libadd.so 104 | $ ./ccint main.cpp -L ./libadd.so 105 | 32 + 64 = 96 106 | ``` 107 | 108 | * specify include paths 109 | 110 | ``` 111 | /* add.h */ 112 | int add(int, int); 113 | 114 | /* add.c */ 115 | 116 | int add(int a, int b) { 117 | return a + b; 118 | } 119 | 120 | /* main.cpp */ 121 | 122 | #include 123 | #include "add.h" 124 | 125 | void ccint_main() { 126 | printf("32 + 64 = %d\n", add(32, 64)); 127 | } 128 | 129 | $ clang -shared -fPIC add.c -o libadd.so 130 | $ mv ./add.h .. 131 | $ ./ccint main.cpp -L ./libadd.so -I .. 132 | 32 + 64 = 96 133 | ``` -------------------------------------------------------------------------------- /Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include "clang/AST/GlobalDecl.h" 4 | #include "clang/Basic/LangOptions.h" 5 | #include "clang/Basic/SourceManager.h" 6 | #include "clang/Lex/Lexer.h" 7 | #include "llvm/BinaryFormat/Magic.h" 8 | 9 | namespace clang { 10 | 11 | class PPLexer : public Lexer { 12 | public: 13 | PPLexer(const LangOptions &LangOpts, llvm::StringRef Code) 14 | : Lexer(SourceLocation(), LangOpts, Code.begin(), Code.begin(), 15 | Code.end()) {} 16 | 17 | bool inPPDirective() const { return ParsingPreprocessorDirective; } 18 | 19 | bool AdvanceTo(clang::Token &Tok, clang::tok::TokenKind kind) { 20 | while (!Lex(Tok)) { 21 | if (Tok.is(kind)) 22 | return false; 23 | } 24 | return true; 25 | } 26 | 27 | bool Lex(clang::Token &Tok) { 28 | bool ret = LexFromRawLexer(Tok); 29 | if (inPPDirective()) { 30 | if (Tok.is(tok::eod)) 31 | ParsingPreprocessorDirective = false; 32 | } else { 33 | if (Tok.is(tok::hash)) { 34 | ParsingPreprocessorDirective = true; 35 | } 36 | } 37 | return ret; 38 | } 39 | }; 40 | 41 | size_t getFileOffset(const clang::Token &Tok) { 42 | return Tok.getLocation().getRawEncoding(); 43 | } 44 | 45 | size_t getWrapPos(const clang::LangOptions &LangOpts, const std::string &Code) { 46 | 47 | PPLexer Lex(LangOpts, Code); 48 | Token token; 49 | 50 | while (true) { 51 | bool atEOF = Lex.Lex(token); 52 | if (Lex.inPPDirective() || token.is(tok::eod)) { 53 | if (atEOF) 54 | break; 55 | continue; 56 | } 57 | 58 | if (token.is(tok::eof)) { 59 | return std::string::npos; 60 | } 61 | 62 | const tok::TokenKind kind = token.getKind(); 63 | 64 | if (kind == tok::raw_identifier) { 65 | StringRef keyword(token.getRawIdentifier()); 66 | if (keyword.equals("using")) { 67 | if (Lex.AdvanceTo(token, tok::semi)) { 68 | return std::string::npos; 69 | } 70 | return getFileOffset(token) + 1; 71 | } 72 | } 73 | 74 | return getFileOffset(token); 75 | } 76 | 77 | return std::string::npos; 78 | } 79 | 80 | bool isCCIntMain(clang::FunctionDecl *FD) { 81 | if (!FD) { 82 | return false; 83 | } 84 | 85 | if (!FD->getDeclName().isIdentifier()) { 86 | return false; 87 | } 88 | return FD->getName().startswith("ccint_main"); 89 | } 90 | 91 | bool isDynamicLibrary(llvm::StringRef Path) { 92 | llvm::file_magic type; 93 | 94 | std::error_code EC = identify_magic(Path, type); 95 | if (EC) { 96 | return false; 97 | } 98 | 99 | switch (type) { 100 | default: 101 | return false; 102 | case llvm::file_magic::macho_fixed_virtual_memory_shared_lib: 103 | case llvm::file_magic::macho_dynamically_linked_shared_lib: 104 | case llvm::file_magic::macho_dynamically_linked_shared_lib_stub: 105 | case llvm::file_magic::elf_shared_object: 106 | case llvm::file_magic::pecoff_executable: 107 | return true; 108 | } 109 | } 110 | 111 | } // namespace clang -------------------------------------------------------------------------------- /CCIntJIT.cpp: -------------------------------------------------------------------------------- 1 | #include "CCIntJIT.h" 2 | #include "clang/Basic/TargetInfo.h" 3 | #include "clang/Basic/TargetOptions.h" 4 | #include "llvm/ExecutionEngine/ExecutionEngine.h" 5 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 6 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 7 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 8 | #include "llvm/ExecutionEngine/Orc/LLJIT.h" 9 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 10 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 11 | #include "llvm/IR/Module.h" 12 | #include "llvm/Support/DynamicLibrary.h" 13 | #include "llvm/Support/Error.h" 14 | #include "llvm/Support/ManagedStatic.h" 15 | #include "llvm/Support/TargetSelect.h" 16 | 17 | namespace clang { 18 | 19 | CCIntJIT::CCIntJIT(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err, 20 | const clang::TargetInfo &TI) 21 | : TSCtx(TSC) { 22 | 23 | using namespace llvm::orc; 24 | llvm::ErrorAsOutParameter EAO(&Err); 25 | 26 | auto JTMB = JITTargetMachineBuilder(TI.getTriple()); 27 | JTMB.addFeatures(TI.getTargetOpts().Features); 28 | if (auto JitOrErr = LLJITBuilder().setJITTargetMachineBuilder(JTMB).create()) 29 | Jit = std::move(*JitOrErr); 30 | else { 31 | Err = JitOrErr.takeError(); 32 | return; 33 | } 34 | 35 | if (auto GeneratorOrErr = DynamicLibrarySearchGenerator::GetForCurrentProcess( 36 | Jit->getDataLayout().getGlobalPrefix())) { 37 | Jit->getMainJITDylib().addGenerator(std::move(*GeneratorOrErr)); 38 | } else { 39 | Err = GeneratorOrErr.takeError(); 40 | return; 41 | } 42 | } 43 | 44 | CCIntJIT::~CCIntJIT() {} 45 | 46 | llvm::Error CCIntJIT::addModule(std::unique_ptr TheModule) { 47 | llvm::orc::ResourceTrackerSP RT = 48 | Jit->getMainJITDylib().createResourceTracker(); 49 | ResourceTrackers[TheModule.get()] = RT; 50 | 51 | return Jit->addIRModule(RT, {std::move(TheModule), TSCtx}); 52 | } 53 | 54 | llvm::Error CCIntJIT::removeModule(std::unique_ptr TheModule) { 55 | 56 | llvm::orc::ResourceTrackerSP RT = 57 | std::move(ResourceTrackers[TheModule.get()]); 58 | if (!RT) 59 | return llvm::Error::success(); 60 | 61 | ResourceTrackers.erase(TheModule.get()); 62 | if (llvm::Error Err = RT->remove()) 63 | return Err; 64 | return llvm::Error::success(); 65 | } 66 | 67 | llvm::Error CCIntJIT::runCtors() const { 68 | return Jit->initialize(Jit->getMainJITDylib()); 69 | } 70 | 71 | llvm::Expected 72 | CCIntJIT::getSymbolAddress(llvm::StringRef Name) const { 73 | auto Sym = Jit->lookup(Name); 74 | 75 | if (!Sym) 76 | return Sym.takeError(); 77 | return Sym->getValue(); 78 | } 79 | 80 | llvm::Error CCIntJIT::AddStaticLib(llvm::StringRef Path) { 81 | auto G = llvm::orc::StaticLibraryDefinitionGenerator::Load( 82 | Jit->getObjLinkingLayer(), Path.data()); 83 | if (!G) 84 | return G.takeError(); 85 | 86 | Jit->getMainJITDylib().addGenerator(std::move(*G)); 87 | 88 | return llvm::Error::success(); 89 | } 90 | 91 | llvm::Error CCIntJIT::AddDynamicLib(llvm::StringRef Path) { 92 | std::string ErrMsg; 93 | if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(Path.data(), &ErrMsg)) { 94 | return llvm::createStringError(llvm::inconvertibleErrorCode(), ErrMsg); 95 | } 96 | 97 | return llvm::Error::success(); 98 | } 99 | 100 | } // end namespace clang 101 | -------------------------------------------------------------------------------- /CCIntParser.cpp: -------------------------------------------------------------------------------- 1 | #include "CCIntParser.h" 2 | #include "Utils.h" 3 | 4 | #include "clang/AST/DeclContextInternals.h" 5 | #include "clang/CodeGen/BackendUtil.h" 6 | #include "clang/CodeGen/CodeGenAction.h" 7 | #include "clang/CodeGen/ModuleBuilder.h" 8 | #include "clang/Frontend/CompilerInstance.h" 9 | #include "clang/Frontend/FrontendAction.h" 10 | #include "clang/FrontendTool/Utils.h" 11 | #include "clang/Lex/Preprocessor.h" 12 | #include "clang/Lex/PreprocessorOptions.h" 13 | #include "clang/Parse/Parser.h" 14 | #include "clang/Sema/Sema.h" 15 | 16 | #include "clang/AST/RecursiveASTVisitor.h" 17 | #include "llvm/Option/ArgList.h" 18 | #include "llvm/Support/CrashRecoveryContext.h" 19 | #include "llvm/Support/Error.h" 20 | #include "llvm/Support/Timer.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace clang { 27 | 28 | class CCIntAction : public WrapperFrontendAction { 29 | private: 30 | std::string MangledName; 31 | 32 | public: 33 | CCIntAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, 34 | llvm::Error &Err) 35 | : WrapperFrontendAction([&]() { 36 | llvm::ErrorAsOutParameter EAO(&Err); 37 | 38 | std::unique_ptr Act; 39 | Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); 40 | return Act; 41 | }()) {} 42 | 43 | FrontendAction *getWrapped() const { return WrappedAction.get(); } 44 | llvm::StringRef GetMangledName() const { return MangledName; }; 45 | 46 | void ExecuteAction() override { 47 | WrapperFrontendAction::ExecuteAction(); 48 | TranslationUnitDecl *TUDecl = 49 | getCompilerInstance().getASTContext().getTranslationUnitDecl(); 50 | 51 | for (Decl *D : TUDecl->decls()) { 52 | if (FunctionDecl *FD = llvm::dyn_cast(D)) { 53 | if (isCCIntMain(FD)) { 54 | CodeGenerator *CG = 55 | static_cast(getWrapped())->getCodeGenerator(); 56 | assert(CG); 57 | MangledName = CG->GetMangledName(FD).str(); 58 | } 59 | } 60 | } 61 | } 62 | }; 63 | 64 | CCIntParser::CCIntParser(std::unique_ptr Instance, 65 | llvm::LLVMContext &LLVMCtx, llvm::Error &Err) 66 | : CI(std::move(Instance)) { 67 | 68 | Act = std::make_unique(*CI, LLVMCtx, Err); 69 | } 70 | 71 | CCIntParser::~CCIntParser() {} 72 | 73 | llvm::Error CCIntParser::Parse(llvm::StringRef FileName, bool Wrap) { 74 | if (!Act) { 75 | return llvm::createStringError(llvm::errc::not_supported, "parse failed"); 76 | } 77 | 78 | FrontendInputFile InputFile(FileName, 79 | CI->getFrontendOpts().Inputs[0].getKind()); 80 | 81 | CI->getInvocation().getFrontendOpts().Inputs.clear(); 82 | CI->getInvocation().getFrontendOpts().Inputs.push_back(InputFile); 83 | 84 | std::string wrapCode; 85 | if (Wrap) { 86 | llvm::ErrorOr> MBOrErr = 87 | llvm::MemoryBuffer::getFile(FileName); 88 | if (std::error_code error = MBOrErr.getError()) { 89 | return llvm::createStringError(llvm::errc::not_supported, 90 | "failed to read file: %s", 91 | error.message().c_str()); 92 | } 93 | 94 | llvm::StringRef Code(MBOrErr.get()->getBufferStart(), 95 | MBOrErr.get()->getBufferSize()); 96 | 97 | wrapCode = WrapInput(Code.str()); 98 | 99 | std::unique_ptr MB = 100 | llvm::MemoryBuffer::getMemBuffer(wrapCode); 101 | CI->getPreprocessorOpts().addRemappedFile(FileName, MB.release()); 102 | } 103 | 104 | bool Success = CI->ExecuteAction(*Act); 105 | if (!Success) { 106 | return llvm::createStringError(llvm::errc::not_supported, "parse failed"); 107 | } 108 | 109 | TheModule = static_cast(Act->getWrapped())->takeModule(); 110 | 111 | return llvm::Error::success(); 112 | } 113 | 114 | llvm::StringRef CCIntParser::GetMangledName() const { 115 | return Act->GetMangledName(); 116 | } 117 | 118 | std::string CCIntParser::WrapInput(const std::string &Code) { 119 | 120 | size_t wrapPos = getWrapPos(CI->getLangOpts(), Code); 121 | 122 | if (wrapPos != std::string::npos) { 123 | std::string res = Code.substr(0, wrapPos) + "void ccint_main() {\n" + 124 | Code.substr(wrapPos) + "\n}"; 125 | 126 | return res; 127 | } 128 | 129 | return Code; 130 | } 131 | 132 | } // end namespace clang 133 | -------------------------------------------------------------------------------- /doc/ccint.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Clang
C/C++ Frontend
Clang...
C/C++
C/C++
LLVM
ORC JIT
LLVM...
LLVM IR
LLVM IR
Text is not SVG - cannot display
-------------------------------------------------------------------------------- /Interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include "Interpreter.h" 2 | #include "CCIntJIT.h" 3 | #include "CCIntParser.h" 4 | 5 | #include "clang/AST/ASTContext.h" 6 | #include "clang/Basic/TargetInfo.h" 7 | #include "clang/CodeGen/ModuleBuilder.h" 8 | #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" 9 | #include "clang/Driver/Compilation.h" 10 | #include "clang/Driver/Driver.h" 11 | #include "clang/Driver/Job.h" 12 | #include "clang/Driver/Options.h" 13 | #include "clang/Driver/Tool.h" 14 | #include "clang/Frontend/CompilerInstance.h" 15 | #include "clang/Frontend/TextDiagnosticBuffer.h" 16 | #include "clang/Lex/HeaderSearch.h" 17 | #include "clang/Lex/HeaderSearchOptions.h" 18 | #include "clang/Lex/Preprocessor.h" 19 | #include "clang/Lex/PreprocessorOptions.h" 20 | #include "llvm/Support/TargetSelect.h" 21 | 22 | #include "llvm/IR/Module.h" 23 | #include "llvm/Support/Errc.h" 24 | #include "llvm/Support/Host.h" 25 | #include 26 | 27 | #include 28 | 29 | using namespace clang; 30 | 31 | namespace { 32 | 33 | static llvm::Expected 34 | GetCC1Arguments(DiagnosticsEngine *Diagnostics, 35 | driver::Compilation *Compilation) { 36 | const driver::JobList &Jobs = Compilation->getJobs(); 37 | 38 | auto IsCC1Command = [](const driver::Command &Cmd) { 39 | return StringRef(Cmd.getCreator().getName()) == "clang"; 40 | }; 41 | 42 | auto IsSrcFile = [](const driver::InputInfo &II) { 43 | return isSrcFile(II.getType()); 44 | }; 45 | 46 | llvm::SmallVector CC1Jobs; 47 | for (const driver::Command &Job : Jobs) 48 | if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile)) 49 | CC1Jobs.push_back(&Job); 50 | 51 | if (CC1Jobs.empty() || (CC1Jobs.size() > 1)) { 52 | return llvm::createStringError(llvm::errc::not_supported, 53 | "driver initialization failed"); 54 | } 55 | 56 | return &CC1Jobs[0]->getArguments(); 57 | } 58 | 59 | static llvm::Expected> 60 | CreateCIInternal(const llvm::opt::ArgStringList &Argv) { 61 | std::unique_ptr CI(new CompilerInstance()); 62 | IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); 63 | 64 | auto PCHOps = CI->getPCHContainerOperations(); 65 | PCHOps->registerWriter(std::make_unique()); 66 | PCHOps->registerReader(std::make_unique()); 67 | 68 | /* 69 | for (auto it = Argv.begin(); it != Argv.end(); ++it) { 70 | std::cout << *it << std::endl; 71 | } 72 | */ 73 | 74 | /* 75 | llvm::InitializeAllTargets(); 76 | llvm::InitializeAllTargetMCs(); 77 | llvm::InitializeAllAsmPrinters(); 78 | llvm::InitializeAllAsmParsers(); 79 | */ 80 | 81 | llvm::InitializeNativeTarget(); 82 | llvm::InitializeNativeTargetAsmPrinter(); 83 | 84 | IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); 85 | TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; 86 | DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); 87 | 88 | bool Success = CompilerInvocation::CreateFromArgs( 89 | CI->getInvocation(), llvm::makeArrayRef(Argv.begin(), Argv.size()), 90 | Diags); 91 | 92 | if (CI->getHeaderSearchOpts().UseBuiltinIncludes && 93 | CI->getHeaderSearchOpts().ResourceDir.empty()) { 94 | CI->getHeaderSearchOpts().ResourceDir = 95 | CompilerInvocation::GetResourcesPath(Argv[0], nullptr); 96 | } 97 | 98 | CI->createDiagnostics(); 99 | if (!CI->hasDiagnostics()) 100 | return llvm::createStringError(llvm::errc::not_supported, 101 | "initialization failed. " 102 | "unable to create diagnostics engine"); 103 | 104 | DiagsBuffer->FlushDiagnostics(CI->getDiagnostics()); 105 | if (!Success) { 106 | CI->getDiagnosticClient().finish(); 107 | return llvm::createStringError(llvm::errc::not_supported, 108 | "initialization failed. " 109 | "unable to flush diagnostics"); 110 | } 111 | 112 | CI->getCodeGenOpts().ClearASTBeforeBackend = false; 113 | CI->getFrontendOpts().DisableFree = false; 114 | CI->getCodeGenOpts().DisableFree = false; 115 | 116 | return std::move(CI); 117 | } 118 | 119 | } // anonymous namespace 120 | llvm::Expected> Interpreter::CreateCI() { 121 | std::vector ClangArgv; 122 | std::string MainExecutableName = 123 | llvm::sys::fs::getMainExecutable(nullptr, nullptr); 124 | 125 | ClangArgv.insert(ClangArgv.begin(), MainExecutableName.c_str()); 126 | 127 | ClangArgv.insert(ClangArgv.begin() + 1, "-c"); 128 | ClangArgv.push_back("-x"); 129 | ClangArgv.push_back("c++"); 130 | ClangArgv.push_back("-D_GLIBCXX_USE_CXX11_ABI=0"); 131 | 132 | ClangArgv.push_back(""); 133 | 134 | IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); 135 | IntrusiveRefCntPtr DiagOpts = 136 | CreateAndPopulateDiagOpts(ClangArgv); 137 | TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; 138 | DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); 139 | 140 | driver::Driver Driver(ClangArgv[0], llvm::sys::getProcessTriple(), Diags); 141 | Driver.setCheckInputsExist(false); 142 | llvm::ArrayRef RF = llvm::makeArrayRef(ClangArgv); 143 | std::unique_ptr Compilation(Driver.BuildCompilation(RF)); 144 | 145 | if (Compilation->getArgs().hasArg(driver::options::OPT_v)) 146 | Compilation->getJobs().Print(llvm::errs(), "\n", false); 147 | 148 | auto ErrOrCC1Args = GetCC1Arguments(&Diags, Compilation.get()); 149 | if (auto Err = ErrOrCC1Args.takeError()) 150 | return std::move(Err); 151 | 152 | return CreateCIInternal(**ErrOrCC1Args); 153 | } 154 | 155 | Interpreter::Interpreter(std::unique_ptr CI, llvm::Error &Err) 156 | : m_WrapInput(false) { 157 | llvm::ErrorAsOutParameter EAO(&Err); 158 | auto LLVMCtx = std::make_unique(); 159 | TSCtx = std::make_unique(std::move(LLVMCtx)); 160 | Parser = 161 | std::make_unique(std::move(CI), *TSCtx->getContext(), Err); 162 | } 163 | 164 | Interpreter::~Interpreter() {} 165 | 166 | llvm::Expected> 167 | Interpreter::create(std::unique_ptr CI) { 168 | llvm::Error Err = llvm::Error::success(); 169 | auto Interp = 170 | std::unique_ptr(new Interpreter(std::move(CI), Err)); 171 | if (Err) 172 | return std::move(Err); 173 | return std::move(Interp); 174 | } 175 | 176 | CompilerInstance *Interpreter::getCompilerInstance() { return Parser->getCI(); } 177 | 178 | std::unique_ptr Interpreter::getModule() { 179 | return Parser->getModule(); 180 | } 181 | 182 | llvm::Error Interpreter::Parse(llvm::StringRef FileName) { 183 | return Parser->Parse(FileName, isWrapInputEnabled()); 184 | } 185 | 186 | llvm::Error Interpreter::Execute() { 187 | if (!Executor) { 188 | const clang::TargetInfo &TI = getCompilerInstance()->getTarget(); 189 | 190 | llvm::Error Err = llvm::Error::success(); 191 | Executor = std::make_unique(*TSCtx, Err, TI); 192 | 193 | if (Err) 194 | return Err; 195 | 196 | for (auto &Path : StaticLibVec) { 197 | if (Err = Executor->AddStaticLib(Path)) { 198 | return Err; 199 | } 200 | } 201 | 202 | for (auto &Path : DynamicLibVec) { 203 | if (Err = Executor->AddDynamicLib(Path)) { 204 | return Err; 205 | } 206 | } 207 | 208 | if (Err = Executor->addModule(std::move(getModule()))) { 209 | return Err; 210 | } 211 | 212 | if (Err = Executor->runCtors()) { 213 | return Err; 214 | } 215 | } 216 | 217 | auto Symbol = getSymbolAddress(); 218 | if (!Symbol) { 219 | return Symbol.takeError(); 220 | } 221 | 222 | int (*fp)() = reinterpret_cast(Symbol.get()); 223 | int ret = fp(); 224 | 225 | return llvm::Error::success(); 226 | } 227 | 228 | llvm::Expected Interpreter::getSymbolAddress() const { 229 | if (!Executor) { 230 | return llvm::createStringError(llvm::errc::not_supported, 231 | "operation failed. no execution engine"); 232 | } 233 | 234 | llvm::StringRef MangledName = Parser->GetMangledName(); 235 | return Executor->getSymbolAddress(MangledName); 236 | } 237 | 238 | void Interpreter::AddIncludePath(llvm::StringRef Path) { 239 | 240 | CompilerInstance *CI = getCompilerInstance(); 241 | HeaderSearchOptions &hso = CI->getHeaderSearchOpts(); 242 | for (const HeaderSearchOptions::Entry &E : hso.UserEntries) { 243 | if ((E.Path == Path)) { 244 | return; 245 | } 246 | } 247 | 248 | hso.AddPath(Path, frontend::Angled, false, false); 249 | } 250 | 251 | void Interpreter::PrintIncludePath() { 252 | CompilerInstance *CI = getCompilerInstance(); 253 | HeaderSearchOptions &hso = CI->getHeaderSearchOpts(); 254 | 255 | for (size_t i = 0; i < hso.UserEntries.size(); ++i) { 256 | llvm::outs() << hso.UserEntries[i].Path << "\n"; 257 | } 258 | } 259 | 260 | void Interpreter::AddStaticLib(llvm::StringRef Path) { 261 | StaticLibVec.push_back(Path.str()); 262 | } 263 | 264 | void Interpreter::AddDynamicLib(llvm::StringRef Path) { 265 | DynamicLibVec.push_back(Path.str()); 266 | } 267 | 268 | void Interpreter::AddHeaderPath(llvm::StringRef Path) { 269 | HeaderPathVec.push_back(Path.str()); 270 | } 271 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------