blob: 105c088b9e91394e71a494dde33d49f032d73070 [file] [log] [blame]
//===-- AppleObjCDeclVendor.cpp ---------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "AppleObjCDeclVendor.h"
#include "Plugins/ExpressionParser/Clang/ASTDumper.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
#include "lldb/Symbol/ClangUtil.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/Log.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
using namespace lldb_private;
class lldb_private::AppleObjCExternalASTSource
: public ClangExternalASTSourceCommon {
public:
AppleObjCExternalASTSource(AppleObjCDeclVendor &decl_vendor)
: m_decl_vendor(decl_vendor) {}
bool FindExternalVisibleDeclsByName(const clang::DeclContext *decl_ctx,
clang::DeclarationName name) override {
static unsigned int invocation_id = 0;
unsigned int current_id = invocation_id++;
Log *log(GetLogIfAllCategoriesSet(
LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
if (log) {
log->Printf("AppleObjCExternalASTSource::FindExternalVisibleDeclsByName[%"
"u] on (ASTContext*)%p Looking for %s in (%sDecl*)%p",
current_id,
static_cast<void *>(&decl_ctx->getParentASTContext()),
name.getAsString().c_str(), decl_ctx->getDeclKindName(),
static_cast<const void *>(decl_ctx));
}
do {
const clang::ObjCInterfaceDecl *interface_decl =
llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl_ctx);
if (!interface_decl)
break;
clang::ObjCInterfaceDecl *non_const_interface_decl =
const_cast<clang::ObjCInterfaceDecl *>(interface_decl);
if (!m_decl_vendor.FinishDecl(non_const_interface_decl))
break;
clang::DeclContext::lookup_result result =
non_const_interface_decl->lookup(name);
return (result.size() != 0);
} while (0);
SetNoExternalVisibleDeclsForName(decl_ctx, name);
return false;
}
void CompleteType(clang::TagDecl *tag_decl) override {
static unsigned int invocation_id = 0;
unsigned int current_id = invocation_id++;
Log *log(GetLogIfAllCategoriesSet(
LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
if (log) {
log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on "
"(ASTContext*)%p Completing (TagDecl*)%p named %s",
current_id, static_cast<void *>(&tag_decl->getASTContext()),
static_cast<void *>(tag_decl),
tag_decl->getName().str().c_str());
log->Printf(" AOEAS::CT[%u] Before:", current_id);
ASTDumper dumper((clang::Decl *)tag_decl);
dumper.ToLog(log, " [CT] ");
}
if (log) {
log->Printf(" AOEAS::CT[%u] After:", current_id);
ASTDumper dumper((clang::Decl *)tag_decl);
dumper.ToLog(log, " [CT] ");
}
return;
}
void CompleteType(clang::ObjCInterfaceDecl *interface_decl) override {
static unsigned int invocation_id = 0;
unsigned int current_id = invocation_id++;
Log *log(GetLogIfAllCategoriesSet(
LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
if (log) {
log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on "
"(ASTContext*)%p Completing (ObjCInterfaceDecl*)%p named %s",
current_id,
static_cast<void *>(&interface_decl->getASTContext()),
static_cast<void *>(interface_decl),
interface_decl->getName().str().c_str());
log->Printf(" AOEAS::CT[%u] Before:", current_id);
ASTDumper dumper((clang::Decl *)interface_decl);
dumper.ToLog(log, " [CT] ");
}
m_decl_vendor.FinishDecl(interface_decl);
if (log) {
log->Printf(" [CT] After:");
ASTDumper dumper((clang::Decl *)interface_decl);
dumper.ToLog(log, " [CT] ");
}
return;
}
bool layoutRecordType(
const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
&BaseOffsets,
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
&VirtualBaseOffsets) override {
return false;
}
void StartTranslationUnit(clang::ASTConsumer *Consumer) override {
clang::TranslationUnitDecl *translation_unit_decl =
m_decl_vendor.m_ast_ctx.getASTContext()->getTranslationUnitDecl();
translation_unit_decl->setHasExternalVisibleStorage();
translation_unit_decl->setHasExternalLexicalStorage();
}
private:
AppleObjCDeclVendor &m_decl_vendor;
};
AppleObjCDeclVendor::AppleObjCDeclVendor(ObjCLanguageRuntime &runtime)
: DeclVendor(), m_runtime(runtime), m_ast_ctx(runtime.GetProcess()
->GetTarget()
.GetArchitecture()
.GetTriple()
.getTriple()
.c_str()),
m_type_realizer_sp(m_runtime.GetEncodingToType()) {
m_external_source = new AppleObjCExternalASTSource(*this);
llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> external_source_owning_ptr(
m_external_source);
m_ast_ctx.getASTContext()->setExternalSource(external_source_owning_ptr);
}
clang::ObjCInterfaceDecl *
AppleObjCDeclVendor::GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa) {
ISAToInterfaceMap::const_iterator iter = m_isa_to_interface.find(isa);
if (iter != m_isa_to_interface.end())
return iter->second;
clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
ObjCLanguageRuntime::ClassDescriptorSP descriptor =
m_runtime.GetClassDescriptorFromISA(isa);
if (!descriptor)
return NULL;
const ConstString &name(descriptor->GetClassName());
clang::IdentifierInfo &identifier_info =
ast_ctx->Idents.get(name.GetStringRef());
clang::ObjCInterfaceDecl *new_iface_decl = clang::ObjCInterfaceDecl::Create(
*ast_ctx, ast_ctx->getTranslationUnitDecl(), clang::SourceLocation(),
&identifier_info, nullptr, nullptr);
ClangASTMetadata meta_data;
meta_data.SetISAPtr(isa);
m_external_source->SetMetadata(new_iface_decl, meta_data);
new_iface_decl->setHasExternalVisibleStorage();
new_iface_decl->setHasExternalLexicalStorage();
ast_ctx->getTranslationUnitDecl()->addDecl(new_iface_decl);
m_isa_to_interface[isa] = new_iface_decl;
return new_iface_decl;
}
class ObjCRuntimeMethodType {
public:
ObjCRuntimeMethodType(const char *types) : m_is_valid(false) {
const char *cursor = types;
enum ParserState { Start = 0, InType, InPos } state = Start;
const char *type = NULL;
int brace_depth = 0;
uint32_t stepsLeft = 256;
while (1) {
if (--stepsLeft == 0) {
m_is_valid = false;
return;
}
switch (state) {
case Start: {
switch (*cursor) {
default:
state = InType;
type = cursor;
break;
case '\0':
m_is_valid = true;
return;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
m_is_valid = false;
return;
}
} break;
case InType: {
switch (*cursor) {
default:
++cursor;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (!brace_depth) {
state = InPos;
if (type) {
m_type_vector.push_back(std::string(type, (cursor - type)));
} else {
m_is_valid = false;
return;
}
type = NULL;
} else {
++cursor;
}
break;
case '[':
case '{':
case '(':
++brace_depth;
++cursor;
break;
case ']':
case '}':
case ')':
if (!brace_depth) {
m_is_valid = false;
return;
}
--brace_depth;
++cursor;
break;
case '\0':
m_is_valid = false;
return;
}
} break;
case InPos: {
switch (*cursor) {
default:
state = InType;
type = cursor;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
++cursor;
break;
case '\0':
m_is_valid = true;
return;
}
} break;
}
}
}
clang::ObjCMethodDecl *
BuildMethod(clang::ObjCInterfaceDecl *interface_decl, const char *name,
bool instance,
ObjCLanguageRuntime::EncodingToTypeSP type_realizer_sp) {
if (!m_is_valid || m_type_vector.size() < 3)
return NULL;
clang::ASTContext &ast_ctx(interface_decl->getASTContext());
const bool isInstance = instance;
const bool isVariadic = false;
const bool isSynthesized = false;
const bool isImplicitlyDeclared = true;
const bool isDefined = false;
const clang::ObjCMethodDecl::ImplementationControl impControl =
clang::ObjCMethodDecl::None;
const bool HasRelatedResultType = false;
const bool for_expression = true;
std::vector<clang::IdentifierInfo *> selector_components;
const char *name_cursor = name;
bool is_zero_argument = true;
while (*name_cursor != '\0') {
const char *colon_loc = strchr(name_cursor, ':');
if (!colon_loc) {
selector_components.push_back(
&ast_ctx.Idents.get(llvm::StringRef(name_cursor)));
break;
} else {
is_zero_argument = false;
selector_components.push_back(&ast_ctx.Idents.get(
llvm::StringRef(name_cursor, colon_loc - name_cursor)));
name_cursor = colon_loc + 1;
}
}
clang::IdentifierInfo **identifier_infos = selector_components.data();
if (!identifier_infos) {
return NULL;
}
clang::Selector sel = ast_ctx.Selectors.getSelector(
is_zero_argument ? 0 : selector_components.size(),
identifier_infos);
clang::QualType ret_type =
ClangUtil::GetQualType(type_realizer_sp->RealizeType(
interface_decl->getASTContext(), m_type_vector[0].c_str(),
for_expression));
if (ret_type.isNull())
return NULL;
clang::ObjCMethodDecl *ret = clang::ObjCMethodDecl::Create(
ast_ctx, clang::SourceLocation(), clang::SourceLocation(), sel,
ret_type, NULL, interface_decl, isInstance, isVariadic, isSynthesized,
isImplicitlyDeclared, isDefined, impControl, HasRelatedResultType);
std::vector<clang::ParmVarDecl *> parm_vars;
for (size_t ai = 3, ae = m_type_vector.size(); ai != ae; ++ai) {
const bool for_expression = true;
clang::QualType arg_type =
ClangUtil::GetQualType(type_realizer_sp->RealizeType(
ast_ctx, m_type_vector[ai].c_str(), for_expression));
if (arg_type.isNull())
return NULL; // well, we just wasted a bunch of time. Wish we could
// delete the stuff we'd just made!
parm_vars.push_back(clang::ParmVarDecl::Create(
ast_ctx, ret, clang::SourceLocation(), clang::SourceLocation(), NULL,
arg_type, NULL, clang::SC_None, NULL));
}
ret->setMethodParams(ast_ctx,
llvm::ArrayRef<clang::ParmVarDecl *>(parm_vars),
llvm::ArrayRef<clang::SourceLocation>());
return ret;
}
explicit operator bool() { return m_is_valid; }
size_t GetNumTypes() { return m_type_vector.size(); }
const char *GetTypeAtIndex(size_t idx) { return m_type_vector[idx].c_str(); }
private:
typedef std::vector<std::string> TypeVector;
TypeVector m_type_vector;
bool m_is_valid;
};
bool AppleObjCDeclVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl) {
Log *log(GetLogIfAllCategoriesSet(
LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
ClangASTMetadata *metadata = m_external_source->GetMetadata(interface_decl);
ObjCLanguageRuntime::ObjCISA objc_isa = 0;
if (metadata)
objc_isa = metadata->GetISAPtr();
if (!objc_isa)
return false;
if (!interface_decl->hasExternalVisibleStorage())
return true;
interface_decl->startDefinition();
interface_decl->setHasExternalVisibleStorage(false);
interface_decl->setHasExternalLexicalStorage(false);
ObjCLanguageRuntime::ClassDescriptorSP descriptor =
m_runtime.GetClassDescriptorFromISA(objc_isa);
if (!descriptor)
return false;
auto superclass_func = [interface_decl,
this](ObjCLanguageRuntime::ObjCISA isa) {
clang::ObjCInterfaceDecl *superclass_decl = GetDeclForISA(isa);
if (!superclass_decl)
return;
FinishDecl(superclass_decl);
clang::ASTContext *context = m_ast_ctx.getASTContext();
interface_decl->setSuperClass(context->getTrivialTypeSourceInfo(
context->getObjCInterfaceType(superclass_decl)));
};
auto instance_method_func =
[log, interface_decl, this](const char *name, const char *types) -> bool {
if (!name || !types)
return false; // skip this one
ObjCRuntimeMethodType method_type(types);
clang::ObjCMethodDecl *method_decl =
method_type.BuildMethod(interface_decl, name, true, m_type_realizer_sp);
if (log)
log->Printf("[ AOTV::FD] Instance method [%s] [%s]", name, types);
if (method_decl)
interface_decl->addDecl(method_decl);
return false;
};
auto class_method_func = [log, interface_decl,
this](const char *name, const char *types) -> bool {
if (!name || !types)
return false; // skip this one
ObjCRuntimeMethodType method_type(types);
clang::ObjCMethodDecl *method_decl = method_type.BuildMethod(
interface_decl, name, false, m_type_realizer_sp);
if (log)
log->Printf("[ AOTV::FD] Class method [%s] [%s]", name, types);
if (method_decl)
interface_decl->addDecl(method_decl);
return false;
};
auto ivar_func = [log, interface_decl,
this](const char *name, const char *type,
lldb::addr_t offset_ptr, uint64_t size) -> bool {
if (!name || !type)
return false;
const bool for_expression = false;
if (log)
log->Printf(
"[ AOTV::FD] Instance variable [%s] [%s], offset at %" PRIx64, name,
type, offset_ptr);
CompilerType ivar_type = m_runtime.GetEncodingToType()->RealizeType(
m_ast_ctx, type, for_expression);
if (ivar_type.IsValid()) {
clang::TypeSourceInfo *const type_source_info = nullptr;
const bool is_synthesized = false;
clang::ObjCIvarDecl *ivar_decl = clang::ObjCIvarDecl::Create(
*m_ast_ctx.getASTContext(), interface_decl, clang::SourceLocation(),
clang::SourceLocation(), &m_ast_ctx.getASTContext()->Idents.get(name),
ClangUtil::GetQualType(ivar_type),
type_source_info, // TypeSourceInfo *
clang::ObjCIvarDecl::Public, 0, is_synthesized);
if (ivar_decl) {
interface_decl->addDecl(ivar_decl);
}
}
return false;
};
if (log) {
ASTDumper method_dumper((clang::Decl *)interface_decl);
log->Printf("[AppleObjCDeclVendor::FinishDecl] Finishing Objective-C "
"interface for %s",
descriptor->GetClassName().AsCString());
}
if (!descriptor->Describe(superclass_func, instance_method_func,
class_method_func, ivar_func))
return false;
if (log) {
ASTDumper method_dumper((clang::Decl *)interface_decl);
log->Printf(
"[AppleObjCDeclVendor::FinishDecl] Finished Objective-C interface");
method_dumper.ToLog(log, " [AOTV::FD] ");
}
return true;
}
uint32_t
AppleObjCDeclVendor::FindDecls(const ConstString &name, bool append,
uint32_t max_matches,
std::vector<clang::NamedDecl *> &decls) {
static unsigned int invocation_id = 0;
unsigned int current_id = invocation_id++;
Log *log(GetLogIfAllCategoriesSet(
LIBLLDB_LOG_EXPRESSIONS)); // FIXME - a more appropriate log channel?
if (log)
log->Printf("AppleObjCDeclVendor::FindTypes [%u] ('%s', %s, %u, )",
current_id, (const char *)name.AsCString(),
append ? "true" : "false", max_matches);
if (!append)
decls.clear();
uint32_t ret = 0;
do {
// See if the type is already in our ASTContext.
clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
clang::IdentifierInfo &identifier_info =
ast_ctx->Idents.get(name.GetStringRef());
clang::DeclarationName decl_name =
ast_ctx->DeclarationNames.getIdentifier(&identifier_info);
clang::DeclContext::lookup_result lookup_result =
ast_ctx->getTranslationUnitDecl()->lookup(decl_name);
if (!lookup_result.empty()) {
if (clang::ObjCInterfaceDecl *result_iface_decl =
llvm::dyn_cast<clang::ObjCInterfaceDecl>(lookup_result[0])) {
if (log) {
clang::QualType result_iface_type =
ast_ctx->getObjCInterfaceType(result_iface_decl);
ASTDumper dumper(result_iface_type);
uint64_t isa_value = LLDB_INVALID_ADDRESS;
ClangASTMetadata *metadata =
m_external_source->GetMetadata(result_iface_decl);
if (metadata)
isa_value = metadata->GetISAPtr();
log->Printf("AOCTV::FT [%u] Found %s (isa 0x%" PRIx64
") in the ASTContext",
current_id, dumper.GetCString(), isa_value);
}
decls.push_back(result_iface_decl);
ret++;
break;
} else {
if (log)
log->Printf("AOCTV::FT [%u] There's something in the ASTContext, but "
"it's not something we know about",
current_id);
break;
}
} else if (log) {
log->Printf("AOCTV::FT [%u] Couldn't find %s in the ASTContext",
current_id, name.AsCString());
}
// It's not. If it exists, we have to put it into our ASTContext.
ObjCLanguageRuntime::ObjCISA isa = m_runtime.GetISA(name);
if (!isa) {
if (log)
log->Printf("AOCTV::FT [%u] Couldn't find the isa", current_id);
break;
}
clang::ObjCInterfaceDecl *iface_decl = GetDeclForISA(isa);
if (!iface_decl) {
if (log)
log->Printf("AOCTV::FT [%u] Couldn't get the Objective-C interface for "
"isa 0x%" PRIx64,
current_id, (uint64_t)isa);
break;
}
if (log) {
clang::QualType new_iface_type =
ast_ctx->getObjCInterfaceType(iface_decl);
ASTDumper dumper(new_iface_type);
log->Printf("AOCTV::FT [%u] Created %s (isa 0x%" PRIx64 ")", current_id,
dumper.GetCString(), (uint64_t)isa);
}
decls.push_back(iface_decl);
ret++;
break;
} while (0);
return ret;
}
clang::ExternalASTMerger::ImporterSource
AppleObjCDeclVendor::GetImporterSource() {
return {*m_ast_ctx.getASTContext(),
*m_ast_ctx.getFileManager(),
m_ast_ctx.GetOriginMap()
};
}