> On Mar 16, 2016, at 3:34 PM, Zachary Turner <ztur...@google.com> wrote:
> 
> Hi Greg,
> 
> could you clarify the difference between the functions ParseTypes,

SymbolFile::ParseTypes() is only used for debugging, don't worry about this one 
unless you need a way to say "parse all types in the supplied context". It can 
help you debug things. It is only called by Module::ParseAllDebugSymbols() and 
this is only used for debugging and it isn't exposed anywhere or used by 
anyone. Just used for initial implementation. Again, see 
Module::ParseAllDebugSymbols() for all the details, but not one calls 
Module::ParseAllDebugSymbols().

> FindTypes

FindType is for finding types by name:

virtual uint32_t
FindTypes (const SymbolContext& sc, 
           const ConstString &name, 
           const CompilerDeclContext *parent_decl_ctx, 
           bool append, 
           uint32_t max_matches, 
           llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, 
           TypeMap& types);

The symbol context can limit the scope of your search if needed. Why? Because 
you might be stopped inside a "test" module that has 100 compile units, and you 
are stopped in compile unit "foo.cpp" in function "bar()". The symbol context 
can specify a scope in which to search. If a function or block is specified, 
you should find the any types in the the block, if you don't find one, proceed 
to the parent block. Keep going up the blocks, then search the class if "bar()" 
is a method, then search the compile unit for any types that match, and then 
fall back to the module. It is usually easiest to just lookup the type and come 
up with N results, and pare down the results after you find them as most times 
you will only find one type so there is no need.

If "parent_decl_ctx" is not NULL, you take all results that made it through the 
"sc" filter and then filter out any results that are not in this 
"parent_decl_ctx". The expression parser often says "I am looking for 
"basic_string" in the parent_decl_ctx that represents "namespace std".

You always check to see if "this" is in the searched_symbol_files set and only 
search if you aren't. This is for module debugging where DWARF files refer to 
types from other DWARF files.


And there is one that is specific to module debugging:

    virtual size_t          FindTypes (const std::vector<CompilerContext> 
&context, bool append, TypeMap& types);

You don't need to implement this one unless you have one PDB file that refers 
to a type in another...




> ResolveTypeUID

Often types you might have a variable whose type id "typedef FooType ...;". 
Because of this, you can give your lldb_private::Variable a lldb_private::Type 
that says "I am a typedef named 'FooType' to the type with UID 0x1234. We don't 
need to resolve the type just yet and if no one needs to explore the type, we 
don't need to resolve the type right away. If anyone does ask for the 
CompilerType from a lldb_private::Type, we can then do a 
"m_symfile->ResolveTypeUID(m_encoding_uid);". So it allows us to just say "your 
type is a type from a symbol file with a user ID of 0x1234" and we can resolve 
that lazily if any only if we ever need to. Another instance where this is 
useful is when you create a lldb_private::Function:

Function (CompileUnit *comp_unit,
          lldb::user_id_t func_uid,
          lldb::user_id_t func_type_uid,
          const Mangled &mangled,
          Type * func_type,
          const AddressRange& range);

Note that you don't need to parse the function type up front, you can just make 
a function with:

SymbolFilePDB::ParseFunction(...)
{
    lldb::user_id_t func_uid = 0x1234;
    Mangled mangled("_Z3foo", true);
    AddressRange func_range = ...;
    Function *func = new Function(m_cu, func_uid, func_uid, mangled, nullptr, 
func_range);
}

Later if anyone calls:

    Type* Function::GetType();

You can see if can lazily resolve its function type using ResolveTypeUID:

Type*
Function::GetType()
{
    if (m_type == nullptr)
    {
        SymbolContext sc;
        
        CalculateSymbolContext (&sc);
        
        if (!sc.module_sp)
            return nullptr;
        
        SymbolVendor *sym_vendor = sc.module_sp->GetSymbolVendor();
        
        if (sym_vendor == nullptr)
            return nullptr;
        
        SymbolFile *sym_file = sym_vendor->GetSymbolFile();
        
        if (sym_file == nullptr)
            return nullptr;
        
        m_type = sym_file->ResolveTypeUID(m_type_uid);
    }
    return m_type;
}

> , and CompleteType

    virtual bool SymbolFile::CompleteType (CompilerType &compiler_type);

This allows you to say "I have a class A type that is very complex and until 
someone needs to know the details about this class I will have the type 
represented as 'class A;'. Once someone needs to know the details they can 
lazily complete the type. This is very useful when creating types in 
clang::ASTContext objects because we can make a forward declaration that knows 
how to complete itself when you hook into the clang::ExternalASTSource. This is 
some of the factoring you have already been doing. If you always just want make 
all of your types completely when you hand them out, then you don't need to do 
this. But since clang types already have all the hooks needed to lazily 
complete types, we take advantage of this. This way you can hand an expression 
a "class A;" as the type for a variable, and if clang ever needs to know about 
the details inside of A, the clang::ExternalASTSource hooks will allow the type 
to lazily complete itself _only_ if ever needed. So if you have an expression 
like "A* a = GetA(); printf("%p\n", a)", clang never need to know what is 
inside of A so it never asks us to complete it. But if you do "A* a = ...; 
a->DoSomething()", clang will ask us to expand the type. Another example is 
imagine you have a local variable in your frame whose type is "A* a;". If we 
display the type in the debugger's variable view, we know this is a pointer and 
unless someone clicks on the disclosure triangle to expand the type, we don't 
need to know what is inside of "A". Variable display is done by using 
CompilerType methods like:

CompilerType t;

const bool omit_empty_base_classes = true;
uint32_t num_children = t.GetNumChildren (omit_empty_base_classes);
for (i in num_children)
{
    CompilerType child_type = t.GetChildCompilerTypeAtIndex (exe_ctx, i, ...);
}

When we call CompilerType::GetNumChildren(), we call into 
ClangASTContext::GetNumChildren(...), which knows we have clang types and it 
also knows that if the type isn't complete and it can complete itself, that we 
can complete the type:

uint32_t
ClangASTContext::GetNumChildren (lldb::opaque_compiler_type_t type, bool 
omit_empty_base_classes)
{
    uint32_t num_children = 0;
    clang::QualType qual_type(GetQualType(type));
    const clang::Type::TypeClass type_class = qual_type->getTypeClass();
    switch (type_class)
    {
        case clang::Type::Record:
            if (GetCompleteQualType (getASTContext(), qual_type))
            {


Note that we are smart and only try to complete types that we know can be 
complete, like Record types for clang. See the function GetCompleteQualType(). 
So the key is here: both LLDB code and the actual clang compiler itself as it 
is compiling expressions has the ability to deal with incomplete types and 
complete them only when necessary.

> from the SymbolFile plugin?

Let me know if you have any questions.

Greg
_______________________________________________
lldb-dev mailing list
lldb-dev@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev

Reply via email to