Add logic to LLDB to figure out the types of member functions of C++ classes. Add plumbing for that all the way up to the SB layer

llvm-svn: 217701
This commit is contained in:
Enrico Granata 2014-09-12 18:45:43 +00:00
parent ab4fe98b4a
commit 190064ad0d
8 changed files with 217 additions and 0 deletions

View File

@ -160,6 +160,12 @@ public:
lldb::SBTypeList
GetFunctionArgumentTypes ();
uint32_t
GetNumberOfMemberFunctions ();
lldb::SBType
GetMemberFunctionAtIndex (uint32_t idx);
const char*
GetName();

View File

@ -334,6 +334,12 @@ public:
ClangASTType
GetFunctionReturnType () const;
size_t
GetNumMemberFunctions () const;
ClangASTType
GetMemberFunctionAtIndex (size_t idx);
ClangASTType
GetLValueReferenceType () const;

View File

@ -239,6 +239,12 @@ public:
lldb::SBTypeList
GetFunctionArgumentTypes ();
uint32_t
GetNumberOfMemberFunctions ();
lldb::SBType
GetMemberFunctionAtIndex (uint32_t idx);
bool
IsTypeComplete ();

View File

@ -252,6 +252,28 @@ SBType::GetFunctionArgumentTypes ()
return sb_type_list;
}
uint32_t
SBType::GetNumberOfMemberFunctions ()
{
if (IsValid())
{
return m_opaque_sp->GetClangASTType(true).GetNumMemberFunctions();
}
return 0;
}
lldb::SBType
SBType::GetMemberFunctionAtIndex (uint32_t idx)
{
SBType sb_func_type;
if (IsValid())
{
ClangASTType func_type(m_opaque_sp->GetClangASTType(true).GetMemberFunctionAtIndex(idx));
sb_func_type = SBType(func_type);
}
return sb_func_type;
}
lldb::SBType
SBType::GetUnqualifiedType()
{

View File

@ -47,6 +47,7 @@
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include <iterator>
#include <mutex>
using namespace lldb;
@ -1741,6 +1742,68 @@ ClangASTType::GetFunctionReturnType () const
return ClangASTType();
}
size_t
ClangASTType::GetNumMemberFunctions () const
{
size_t num_functions = 0;
if (IsValid())
{
clang::QualType qual_type(GetCanonicalQualType());
switch (qual_type->getTypeClass()) {
case clang::Type::Record:
if (GetCompleteQualType (m_ast, qual_type))
{
const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr());
const clang::RecordDecl *record_decl = record_type->getDecl();
assert(record_decl);
const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
if (cxx_record_decl)
num_functions = std::distance(cxx_record_decl->method_begin(), cxx_record_decl->method_end());
break;
}
default:
break;
}
}
return num_functions;
}
ClangASTType
ClangASTType::GetMemberFunctionAtIndex (size_t idx)
{
if (IsValid())
{
clang::QualType qual_type(GetCanonicalQualType());
switch (qual_type->getTypeClass()) {
case clang::Type::Record:
if (GetCompleteQualType (m_ast, qual_type))
{
const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr());
const clang::RecordDecl *record_decl = record_type->getDecl();
assert(record_decl);
const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
if (cxx_record_decl)
{
auto method_iter = cxx_record_decl->method_begin();
auto method_end = cxx_record_decl->method_end();
if (idx < std::distance(method_iter, method_end))
{
std::advance(method_iter, idx);
auto method_decl = method_iter->getCanonicalDecl();
if (method_decl)
return ClangASTType(m_ast,method_decl->getType().getAsOpaquePtr());
}
}
}
default:
break;
}
}
return ClangASTType();
}
ClangASTType
ClangASTType::GetLValueReferenceType () const

View File

@ -0,0 +1,5 @@
LEVEL = ../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,81 @@
"""
Test SBType and SBTypeList API.
"""
import os, time
import re
import unittest2
import lldb, lldbutil
from lldbtest import *
class TypeAndTypeListTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@python_api_test
@dsym_test
def test_with_dsym(self):
"""Exercise SBType and SBTypeList API."""
d = {'EXE': self.exe_name}
self.buildDsym(dictionary=d)
self.setTearDownCleanup(dictionary=d)
self.type_api(self.exe_name)
@python_api_test
@dwarf_test
def test_with_dwarf(self):
"""Exercise SBType and SBTypeList API."""
d = {'EXE': self.exe_name}
self.buildDwarf(dictionary=d)
self.setTearDownCleanup(dictionary=d)
self.type_api(self.exe_name)
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# We'll use the test method name as the exe_name.
self.exe_name = self.testMethodName
# Find the line number to break at.
self.source = 'main.cpp'
self.line = line_number(self.source, '// set breakpoint here')
def type_api(self, exe_name):
"""Exercise SBType and SBTypeList API."""
exe = os.path.join(os.getcwd(), exe_name)
# Create a target by the debugger.
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
# Create the breakpoint inside function 'main'.
breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
self.assertTrue(breakpoint, VALID_BREAKPOINT)
# Now launch the process, and do not stop at entry point.
process = target.LaunchSimple (None, None, self.get_process_working_directory())
self.assertTrue(process, PROCESS_IS_VALID)
# Get Frame #0.
self.assertTrue(process.GetState() == lldb.eStateStopped)
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition")
frame0 = thread.GetFrameAtIndex(0)
variable = frame0.FindVariable("d")
Derived = variable.GetType()
Base = Derived.GetDirectBaseClassAtIndex(0).GetType()
self.assertTrue(Derived.GetNumberOfMemberFunctions() == 2, "Derived declares two methods")
self.assertTrue(Derived.GetMemberFunctionAtIndex(0).GetFunctionReturnType().GetName() == "int", "Derived::dImpl returns int")
self.assertTrue(Base.GetNumberOfMemberFunctions() == 4, "Base declares three methods")
self.assertTrue(Base.GetMemberFunctionAtIndex(3).GetFunctionArgumentTypes().GetSize() == 3, "Base::sfunc takes three arguments")
self.assertTrue(Base.GetMemberFunctionAtIndex(2).GetFunctionArgumentTypes().GetSize() == 0, "Base::dat takes no arguments")
self.assertTrue(Base.GetMemberFunctionAtIndex(1).GetFunctionArgumentTypes().GetTypeAtIndex(1).GetName() == "char", "Base::bar takes a second 'char' argument")
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,28 @@
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
class Base {
public:
int foo(int x, int y) { return 1; }
char bar(int x, char y) { return 2; }
void dat() {}
static int sfunc(char, int, float) { return 3; }
};
class Derived: public Base {
protected:
int dImpl() { return 1; }
public:
float baz(float b) { return b + 1.0; }
};
int main() {
Derived d;
return 0; // set breakpoint here
}