//------------------------------------------------------------------------------ // CLING - the C++ LLVM-based InterpreterG :) // version: $Id$ // author: Axel Naumann //------------------------------------------------------------------------------ #include "IncrementalParser.h" #include "ASTDumper.h" #include "ASTNodeEraser.h" #include "DeclCollector.h" #include "DeclExtractor.h" #include "DynamicLookup.h" #include "ValuePrinterSynthesizer.h" #include "cling/Interpreter/CIFactory.h" #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/InterpreterCallbacks.h" #include "cling/Interpreter/Transaction.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" #include "clang/Basic/FileManager.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Parse/Parser.h" #include "clang/Lex/Preprocessor.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_os_ostream.h" #include #include #include #include using namespace clang; namespace cling { IncrementalParser::IncrementalParser(Interpreter* interp, int argc, const char* const *argv, const char* llvmdir): m_Interpreter(interp), m_Consumer(0) { CompilerInstance* CI = CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"), argc, argv, llvmdir); assert(CI && "CompilerInstance is (null)!"); m_Consumer = dyn_cast(&CI->getASTConsumer()); assert(m_Consumer && "Expected ChainedConsumer!"); m_CI.reset(CI); if (CI->getFrontendOpts().ProgramAction != clang::frontend::ParseSyntaxOnly){ m_CodeGen.reset(CreateLLVMCodeGen(CI->getDiagnostics(), "cling input", CI->getCodeGenOpts(), *m_Interpreter->getLLVMContext() )); } CreateSLocOffsetGenerator(); // Add transformers to the IncrementalParser, which owns them m_TTransformers.push_back(new EvaluateTSynthesizer(&CI->getSema())); m_TTransformers.push_back(new DeclExtractor(&getCI()->getSema())); m_TTransformers.push_back(new ValuePrinterSynthesizer(&CI->getSema(), 0)); m_TTransformers.push_back(new ASTDumper()); m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(), false /*skipFuncBodies*/)); CI->getPreprocessor().EnterMainSourceFile(); // Initialize the parser after we have entered the main source file. m_Parser->Initialize(); // Perform initialization that occurs after the parser has been initialized // but before it parses anything. Initializes the consumers too. CI->getSema().Initialize(); } IncrementalParser::~IncrementalParser() { if (hasCodeGenerator()) { getCodeGenerator()->ReleaseModule(); } for (size_t i = 0; i < m_Transactions.size(); ++i) delete m_Transactions[i]; for (size_t i = 0; i < m_TTransformers.size(); ++i) delete m_TTransformers[i]; } // pin the vtable here since there is no point to create dedicated to that // cpp file. TransactionTransformer::~TransactionTransformer() {} void IncrementalParser::beginTransaction(const CompilationOptions& Opts) { llvm::Module* M = 0; if (hasCodeGenerator()) M = getCodeGenerator()->GetModule(); Transaction* NewCurT = new Transaction(Opts, M); Transaction* OldCurT = m_Consumer->getTransaction(); m_Consumer->setTransaction(NewCurT); // If we are in the middle of transaction and we see another begin // transaction - it must be nested transaction. if (OldCurT && !OldCurT->isCompleted()) { OldCurT->addNestedTransaction(NewCurT); // takes the ownership return; } m_Transactions.push_back(NewCurT); } void IncrementalParser::endTransaction() const { Transaction* CurT = m_Consumer->getTransaction(); CurT->setCompleted(); const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics(); //TODO: Make the enum orable. if (Diags.getNumWarnings() > 0) CurT->setIssuedDiags(Transaction::kWarnings); if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) CurT->setIssuedDiags(Transaction::kErrors); if (CurT->isNestedTransaction()) { assert(!CurT->getParent()->isCompleted() && "Parent transaction completed!?"); CurT = m_Consumer->getTransaction()->getParent(); } } void IncrementalParser::commitCurrentTransaction() { Transaction* CurT = m_Consumer->getTransaction(); assert(CurT->isCompleted() && "Transaction not ended!?"); // Check for errors... if (CurT->getIssuedDiags() == Transaction::kErrors) { rollbackTransaction(CurT); return; } // We are sure it's safe to pipe it through the transformers for (size_t i = 0; i < m_TTransformers.size(); ++i) if (!m_TTransformers[i]->TransformTransaction(*CurT)) { // Roll back on error in a transformer rollbackTransaction(CurT); return; } // Pull all template instantiations in that came from the consumers. getCI()->getSema().PerformPendingInstantiations(); m_Consumer->HandleTranslationUnit(getCI()->getASTContext()); if (CurT->getCompilationOpts().CodeGeneration && hasCodeGenerator()) { // Reset the module builder to clean up global initializers, c'tors, d'tors getCodeGenerator()->Initialize(getCI()->getASTContext()); // codegen the transaction for (Transaction::const_iterator I = CurT->decls_begin(), E = CurT->decls_end(); I != E; ++I) { getCodeGenerator()->HandleTopLevelDecl(*I); } getCodeGenerator()->HandleTranslationUnit(getCI()->getASTContext()); // run the static initializers that came from codegenning m_Interpreter->runStaticInitializersOnce(); } CurT->setState(Transaction::kCommitted); InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks(); if (callbacks && callbacks->isEnabled()) callbacks->TransactionCommitted(*CurT); } void IncrementalParser::rollbackTransaction(Transaction* T) const { ASTNodeEraser NodeEraser(&getCI()->getSema()); if (NodeEraser.RevertTransaction(T)) T->setState(Transaction::kRolledBack); else T->setState(Transaction::kRolledBackWithErrors); } // Each input line is contained in separate memory buffer. The SourceManager // assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry // for the MemoryBuffer's FileID. That in turn is problem because invalid // SourceLocations are given to the diagnostics. Thus the diagnostics cannot // order the overloads, for example // // Our work-around is creating a virtual file, which doesn't exist on the disk // with enormous size (no allocation is done). That file has valid FileEntry // and so on... We use it for generating valid SourceLocations with valid // offsets so that it doesn't cause any troubles to the diagnostics. // // +---------------------+ // | Main memory buffer | // +---------------------+ // | Virtual file SLoc | // | address space |<-----------------+ // | ... |<------------+ | // | ... | | | // | ... |<----+ | | // | ... | | | | // +~~~~~~~~~~~~~~~~~~~~~+ | | | // | input_line_1 | ....+.......+..--+ // +---------------------+ | | // | input_line_2 | ....+.....--+ // +---------------------+ | // | ... | | // +---------------------+ | // | input_line_N | ..--+ // +---------------------+ // void IncrementalParser::CreateSLocOffsetGenerator() { SourceManager& SM = getCI()->getSourceManager(); FileManager& FM = SM.getFileManager(); const FileEntry* FE = FM.getVirtualFile("Interactive/InputLineIncluder", 1U << 15U, time(0)); m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?"); } IncrementalParser::EParseResult IncrementalParser::Compile(llvm::StringRef input, const CompilationOptions& Opts) { beginTransaction(Opts); EParseResult Result = ParseInternal(input); endTransaction(); // Check for errors coming from our custom consumers. DiagnosticConsumer& DClient = m_CI->getDiagnosticClient(); DClient.BeginSourceFile(getCI()->getLangOpts(), &getCI()->getPreprocessor()); commitCurrentTransaction(); DClient.EndSourceFile(); m_CI->getDiagnostics().Reset(); return Result; } Transaction* IncrementalParser::Parse(llvm::StringRef input) { beginTransaction(CompilationOptions()); ParseInternal(input); endTransaction(); return getLastTransaction(); } // Add the input to the memory buffer, parse it, and add it to the AST. IncrementalParser::EParseResult IncrementalParser::ParseInternal(llvm::StringRef input) { if (input.empty()) return IncrementalParser::kSuccess; PrettyStackTraceParserEntry CrashInfo(*m_Parser.get()); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar CleanupParser(m_Parser.get()); Sema& S = getCI()->getSema(); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar CleanupSema(&S); Preprocessor& PP = m_CI->getPreprocessor(); DiagnosticConsumer& DClient = m_CI->getDiagnosticClient(); if (!PP.getCurrentLexer()) { PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(), 0, SourceLocation()); } PP.enableIncrementalProcessing(); DClient.BeginSourceFile(m_CI->getLangOpts(), &PP); std::ostringstream source_name; source_name << "input_line_" << (m_MemoryBuffers.size() + 1); // Create an uninitialized memory buffer, copy code in and append "\n" size_t InputSize = input.size(); // don't include trailing 0 // MemBuffer size should *not* include terminating zero llvm::MemoryBuffer* MB = llvm::MemoryBuffer::getNewUninitMemBuffer(InputSize + 1, source_name.str()); char* MBStart = const_cast(MB->getBufferStart()); memcpy(MBStart, input.data(), InputSize); memcpy(MBStart + InputSize, "\n", 2); m_MemoryBuffers.push_back(MB); SourceManager& SM = getCI()->getSourceManager(); // Create SourceLocation, which will allow clang to order the overload // candidates for example SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID); NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffers.size() + 1); // Create FileID for the current buffer FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffers.back(), /*LoadedID*/0, /*LoadedOffset*/0, NewLoc); PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc); Parser::DeclGroupPtrTy ADecl; while (!m_Parser->ParseTopLevelDecl(ADecl)) { // If we got a null return and something *was* parsed, ignore it. This // is due to a top-level semicolon, an action override, or a parse error // skipping something. if (ADecl) m_Consumer->HandleTopLevelDecl(ADecl.getAsVal()); }; // Process any TopLevelDecls generated by #pragma weak. for (llvm::SmallVector::iterator I = S.WeakTopLevelDecls().begin(), E = S.WeakTopLevelDecls().end(); I != E; ++I) { m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I)); } S.PerformPendingInstantiations(); DClient.EndSourceFile(); PP.enableIncrementalProcessing(false); DiagnosticsEngine& Diag = S.getDiagnostics(); if (Diag.hasErrorOccurred()) return IncrementalParser::kFailed; else if (Diag.getNumWarnings()) return IncrementalParser::kSuccessWithWarnings; return IncrementalParser::kSuccess; } void IncrementalParser::unloadTransaction(Transaction* T) { if (!T) T = getLastTransaction(); assert(T->getState() == Transaction::kCommitted && "Unloading not commited transaction?"); assert(T->getModule() && "Trying to uncodegen transaction taken in syntax only mode. "); ASTNodeEraser NodeEraser(&getCI()->getSema()); NodeEraser.RevertTransaction(T); InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks(); if (callbacks && callbacks->isEnabled()) callbacks->TransactionUnloaded(*T); } } // namespace cling