Added support for locating and importing functions

(including inline functions) from modules in the
expression parser.  We now have to retain a reference
to the code generator in ClangExpressionDeclMap so
that any imported function bodies can be appropriately
sent to that code generator.

<rdar://problem/19883002>

llvm-svn: 236297
This commit is contained in:
Sean Callanan 2015-05-01 00:47:29 +00:00
parent 65b5b01d56
commit 80c9759ef7
9 changed files with 193 additions and 11 deletions

View File

@ -100,6 +100,9 @@ public:
WillParse (ExecutionContext &exe_ctx, WillParse (ExecutionContext &exe_ctx,
Materializer *materializer); Materializer *materializer);
void
InstallCodeGenerator (clang::ASTConsumer *code_gen);
//------------------------------------------------------------------ //------------------------------------------------------------------
/// [Used by ClangExpressionParser] For each variable that had an unknown /// [Used by ClangExpressionParser] For each variable that had an unknown
/// type at the beginning of parsing, determine its final type now. /// type at the beginning of parsing, determine its final type now.
@ -396,11 +399,6 @@ private:
{ {
public: public:
ParserVars(ClangExpressionDeclMap &decl_map) : ParserVars(ClangExpressionDeclMap &decl_map) :
m_exe_ctx(),
m_sym_ctx(),
m_persistent_vars(NULL),
m_enable_lookups(false),
m_materializer(NULL),
m_decl_map(decl_map) m_decl_map(decl_map)
{ {
} }
@ -415,12 +413,13 @@ private:
return NULL; return NULL;
} }
ExecutionContext m_exe_ctx; ///< The execution context to use when parsing. ExecutionContext m_exe_ctx; ///< The execution context to use when parsing.
SymbolContext m_sym_ctx; ///< The symbol context to use in finding variables and types. SymbolContext m_sym_ctx; ///< The symbol context to use in finding variables and types.
ClangPersistentVariables *m_persistent_vars; ///< The persistent variables for the process. ClangPersistentVariables *m_persistent_vars = nullptr; ///< The persistent variables for the process.
bool m_enable_lookups; ///< Set to true during parsing if we have found the first "$__lldb" name. bool m_enable_lookups = false; ///< Set to true during parsing if we have found the first "$__lldb" name.
TargetInfo m_target_info; ///< Basic information about the target. TargetInfo m_target_info; ///< Basic information about the target.
Materializer *m_materializer; ///< If non-NULL, the materializer to use when reporting used variables. Materializer *m_materializer = nullptr; ///< If non-NULL, the materializer to use when reporting used variables.
clang::ASTConsumer *m_code_gen = nullptr; ///< If non-NULL, a code generator that receives new top-level functions.
private: private:
ClangExpressionDeclMap &m_decl_map; ClangExpressionDeclMap &m_decl_map;
DISALLOW_COPY_AND_ASSIGN (ParserVars); DISALLOW_COPY_AND_ASSIGN (ParserVars);

View File

@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "lldb/Expression/ClangExpressionDeclMap.h" #include "lldb/Expression/ClangExpressionDeclMap.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/DeclarationName.h" #include "clang/AST/DeclarationName.h"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
@ -110,6 +111,13 @@ ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx,
return true; return true;
} }
void
ClangExpressionDeclMap::InstallCodeGenerator (clang::ASTConsumer *code_gen)
{
assert(m_parser_vars);
m_parser_vars->m_code_gen = code_gen;
}
void void
ClangExpressionDeclMap::DidParse() ClangExpressionDeclMap::DidParse()
{ {
@ -1487,6 +1495,62 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context,
} }
} }
} }
if (!context.m_found.function_with_type_info)
{
// Try the modules next.
do
{
if (ClangModulesDeclVendor *modules_decl_vendor = m_target->GetClangModulesDeclVendor())
{
bool append = false;
uint32_t max_matches = 1;
std::vector <clang::NamedDecl *> decls;
if (!modules_decl_vendor->FindDecls(name,
append,
max_matches,
decls))
break;
clang::NamedDecl *const decl_from_modules = decls[0];
if (llvm::isa<clang::FunctionDecl>(decl_from_modules))
{
if (log)
{
log->Printf(" CAS::FEVD[%u] Matching function found for \"%s\" in the modules",
current_id,
name.GetCString());
}
clang::Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, &decl_from_modules->getASTContext(), decl_from_modules);
clang::FunctionDecl *copied_function_decl = copied_decl ? dyn_cast<clang::FunctionDecl>(copied_decl) : nullptr;
if (!copied_function_decl)
{
if (log)
log->Printf(" CAS::FEVD[%u] - Couldn't export a function declaration from the modules",
current_id);
break;
}
if (copied_function_decl->getBody() && m_parser_vars->m_code_gen)
{
DeclGroupRef decl_group_ref(copied_function_decl);
m_parser_vars->m_code_gen->HandleTopLevelDecl(decl_group_ref);
}
context.AddNamedDecl(copied_function_decl);
context.m_found.function_with_type_info = true;
context.m_found.function = true;
}
}
} while (0);
}
if (target && !context.m_found.variable && !namespace_decl) if (target && !context.m_found.variable && !namespace_decl)
{ {

View File

@ -409,6 +409,9 @@ ClangExpressionParser::Parse (Stream &stream)
ASTConsumer *ast_transformer = m_expr.ASTTransformer(m_code_generator.get()); ASTConsumer *ast_transformer = m_expr.ASTTransformer(m_code_generator.get());
if (ClangExpressionDeclMap *decl_map = m_expr.DeclMap())
decl_map->InstallCodeGenerator(m_code_generator.get());
if (ast_transformer) if (ast_transformer)
ParseAST(m_compiler->getPreprocessor(), ast_transformer, m_compiler->getASTContext()); ParseAST(m_compiler->getPreprocessor(), ast_transformer, m_compiler->getASTContext());
else else

View File

@ -0,0 +1,9 @@
LEVEL = ../../../make
C_SOURCES := myModule.c
OBJC_SOURCES := main.m
include $(LEVEL)/Makefile.rules
CFLAGS += -fmodules -I$(PWD)

View File

@ -0,0 +1,80 @@
"""Test that inline functions from modules are imported correctly"""
import os, time
import unittest2
import lldb
import platform
import lldbutil
from distutils.version import StrictVersion
from lldbtest import *
class ModulesInlineFunctionsTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@skipUnlessDarwin
@dsym_test
def test_expr_with_dsym(self):
self.buildDsym()
self.expr()
@dwarf_test
@skipIfFreeBSD
@skipIfLinux
def test_expr_with_dwarf(self):
self.buildDwarf()
self.expr()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number to break inside main().
self.line = line_number('main.m', '// Set breakpoint here.')
def applies(self):
if platform.system() != "Darwin":
return False
if StrictVersion('12.0.0') > platform.release():
return False
return True
def common_setup(self):
exe = os.path.join(os.getcwd(), "a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
# Break inside the foo function which takes a bar_ptr argument.
lldbutil.run_break_set_by_file_and_line (self, "main.m", self.line, num_expected_locations=1, loc_exact=True)
self.runCmd("run", RUN_SUCCEEDED)
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# The breakpoint should have a hit count of 1.
self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
substrs = [' resolved, hit count = 1'])
def expr(self):
if not self.applies():
return
self.common_setup()
self.runCmd("settings set target.clang-module-search-paths \"" + os.getcwd() + "\"")
self.expect("expr @import myModule; 3", VARIABLES_DISPLAYED_CORRECTLY,
substrs = ["int", "3"])
self.expect("expr isInline(2)", VARIABLES_DISPLAYED_CORRECTLY,
substrs = ["4"])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,9 @@
@import Darwin;
@import myModule;
int main()
{
int a = isInline(2);
int b = notInline();
printf("%d %d\n", a, b); // Set breakpoint here.
}

View File

@ -0,0 +1,4 @@
module myModule {
header "myModule.h"
export *
}

View File

@ -0,0 +1,7 @@
#include "myModule.h"
int notInline()
{
return 3;
}

View File

@ -0,0 +1,7 @@
int notInline();
static __inline__ __attribute__ ((always_inline)) int isInline(int a)
{
int b = a + a;
return b;
}