Fixed a serious bug in DeportType where the types could retain DeclContexts that

pointed into the artificial function constructed for the expression.  I now make
anything that pointed to the function as its DeclContext be global while the
copy occurs; afterward I restored the old DeclContext.

Added a testcase that make sure that this works properly and doesn't crash
anything.

<rdar://problem/21049838>

llvm-svn: 241695
This commit is contained in:
Sean Callanan 2015-07-08 18:03:41 +00:00
parent e086afa667
commit 83b8ad0eaa
2 changed files with 146 additions and 0 deletions

View File

@ -17,6 +17,7 @@
#include "lldb/Symbol/ClangASTImporter.h"
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
#include "lldb/Symbol/ClangNamespaceDecl.h"
#include "lldb/Utility/LLDBAssert.h"
using namespace lldb_private;
using namespace clang;
@ -109,6 +110,134 @@ ClangASTImporter::CopyDecl (clang::ASTContext *dst_ast,
return nullptr;
}
class DeclContextOverride
{
private:
struct Backup
{
clang::DeclContext *decl_context;
clang::DeclContext *lexical_decl_context;
};
std::map<clang::Decl *, Backup> m_backups;
void OverrideOne(clang::Decl *decl)
{
if (m_backups.find(decl) != m_backups.end())
{
return;
}
m_backups[decl] = { decl->getDeclContext(), decl->getLexicalDeclContext() };
decl->setDeclContext(decl->getASTContext().getTranslationUnitDecl());
decl->setLexicalDeclContext(decl->getASTContext().getTranslationUnitDecl());
}
bool ChainPassesThrough(clang::Decl *decl,
clang::DeclContext *base,
clang::DeclContext *(clang::Decl::*contextFromDecl)(),
clang::DeclContext *(clang::DeclContext::*contextFromContext)())
{
for (DeclContext *decl_ctx = (decl->*contextFromDecl)();
decl_ctx;
decl_ctx = (decl_ctx->*contextFromContext)())
{
if (decl_ctx == base)
{
return true;
}
}
return false;
}
clang::Decl *GetEscapedChild(clang::Decl *decl, clang::DeclContext *base = nullptr)
{
if (base)
{
// decl's DeclContext chains must pass through base.
if (!ChainPassesThrough(decl, base, &clang::Decl::getDeclContext, &clang::DeclContext::getParent) ||
!ChainPassesThrough(decl, base, &clang::Decl::getLexicalDeclContext, &clang::DeclContext::getLexicalParent))
{
return decl;
}
}
else
{
base = clang::dyn_cast<clang::DeclContext>(decl);
if (!base)
{
return nullptr;
}
}
if (clang::DeclContext *context = clang::dyn_cast<clang::DeclContext>(decl))
{
for (clang::Decl *decl : context->decls())
{
if (clang::Decl *escaped_child = GetEscapedChild(decl))
{
return escaped_child;
}
}
}
return nullptr;
}
void Override(clang::Decl *decl)
{
if (clang::Decl *escaped_child = GetEscapedChild(decl))
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
if (log)
log->Printf(" [ClangASTImporter] DeclContextOverride couldn't override (%sDecl*)%p - its child (%sDecl*)%p escapes",
decl->getDeclKindName(), static_cast<void*>(decl),
escaped_child->getDeclKindName(), static_cast<void*>(escaped_child));
lldbassert(0 && "Couldn't override!");
}
OverrideOne(decl);
}
public:
DeclContextOverride()
{
}
void OverrideAllDeclsFromContainingFunction(clang::Decl *decl)
{
for (DeclContext *decl_context = decl->getLexicalDeclContext();
decl_context;
decl_context = decl_context->getLexicalParent())
{
DeclContext *redecl_context = decl_context->getRedeclContext();
if (llvm::isa<FunctionDecl>(redecl_context) &&
llvm::isa<TranslationUnitDecl>(redecl_context->getLexicalParent()))
{
for (clang::Decl *child_decl : decl_context->decls())
{
Override(child_decl);
}
}
}
}
~DeclContextOverride()
{
for (const std::pair<clang::Decl *, Backup> &backup : m_backups)
{
backup.first->setDeclContext(backup.second.decl_context);
backup.first->setLexicalDeclContext(backup.second.lexical_decl_context);
}
}
};
lldb::clang_type_t
ClangASTImporter::DeportType (clang::ASTContext *dst_ctx,
clang::ASTContext *src_ctx,
@ -122,6 +251,13 @@ ClangASTImporter::DeportType (clang::ASTContext *dst_ctx,
std::set<NamedDecl *> decls_to_deport;
std::set<NamedDecl *> decls_already_deported;
DeclContextOverride decl_context_override;
if (const clang::TagType *tag_type = clang::QualType::getFromOpaquePtr(type)->getAs<TagType>())
{
decl_context_override.OverrideAllDeclsFromContainingFunction(tag_type->getDecl());
}
minion_sp->InitDeportWorkQueues(&decls_to_deport,
&decls_already_deported);
@ -157,6 +293,10 @@ ClangASTImporter::DeportDecl (clang::ASTContext *dst_ctx,
std::set<NamedDecl *> decls_to_deport;
std::set<NamedDecl *> decls_already_deported;
DeclContextOverride decl_context_override;
decl_context_override.OverrideAllDeclsFromContainingFunction(decl);
minion_sp->InitDeportWorkQueues(&decls_to_deport,
&decls_already_deported);

View File

@ -46,6 +46,12 @@ class PersistenttypesTestCase(TestBase):
self.expect("expression struct { int a; int b; } x = { 2, 3 }; x",
substrs = ['a = 2', 'b = 3'])
self.expect("expression struct { int x; int y; int z; } object; object.y = 1; object.z = 3; object.x = 2; object",
substrs = ['x = 2', 'y = 1', 'z = 3'])
self.expect("expression struct A { int x; int y; }; struct { struct A a; int z; } object; object.a.y = 1; object.z = 3; object.a.x = 2; object",
substrs = ['x = 2', 'y = 1', 'z = 3'])
if __name__ == '__main__':
import atexit