| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "abi_wrappers.h" |
| |
| #include <clang/Tooling/Core/QualTypeNames.h> |
| |
| #include <string> |
| |
| using namespace abi_wrapper; |
| |
| ABIWrapper::ABIWrapper( |
| clang::MangleContext *mangle_contextp, |
| clang::ASTContext *ast_contextp, |
| const clang::CompilerInstance *cip) |
| : cip_(cip), |
| mangle_contextp_(mangle_contextp), |
| ast_contextp_(ast_contextp) { } |
| |
| std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl, |
| const clang::CompilerInstance *cip) { |
| clang::SourceManager &sm = cip->getSourceManager(); |
| clang::SourceLocation location = decl->getLocation(); |
| llvm::StringRef file_name = sm.getFilename(location); |
| llvm::SmallString<128> abs_path(file_name.str()); |
| if (llvm::sys::fs::make_absolute(abs_path)) { |
| return ""; |
| } |
| return abs_path.str(); |
| } |
| |
| abi_dump::AccessSpecifier ABIWrapper::AccessClangToDump( |
| const clang::AccessSpecifier sp) const { |
| switch (sp) { |
| case clang::AS_private: { |
| return abi_dump::AccessSpecifier::private_access; |
| break; |
| } |
| case clang::AS_protected: { |
| return abi_dump::AccessSpecifier::protected_access; |
| break; |
| } |
| default: { |
| return abi_dump::AccessSpecifier::public_access; |
| break; |
| } |
| } |
| } |
| |
| bool ABIWrapper::SetupBasicTypeAbi(abi_dump::BasicTypeAbi *type_abi, |
| const clang::QualType type) const { |
| if (!type_abi) { |
| return false; |
| } |
| const clang::QualType canonical_type = type.getCanonicalType(); |
| type_abi->set_name(QualTypeToString(canonical_type)); |
| // Cannot determine the size and alignment for template parameter dependent |
| // types as well as incomplete types. |
| const clang::Type *base_type = canonical_type.getTypePtr(); |
| clang::Type::TypeClass type_class = base_type->getTypeClass(); |
| // Temporary Hack for auto type sizes. Not determinable. |
| if (base_type && !(base_type->isDependentType()) && |
| !(base_type->isIncompleteType()) && (type_class != clang::Type::Auto)) { |
| std::pair<clang::CharUnits, clang::CharUnits> size_and_alignment = |
| ast_contextp_->getTypeInfoInChars(canonical_type); |
| int64_t size = size_and_alignment.first.getQuantity(); |
| int64_t alignment = size_and_alignment.second.getQuantity(); |
| type_abi->set_size(size); |
| type_abi->set_alignment(alignment); |
| } |
| return true; |
| } |
| |
| bool ABIWrapper::SetupBasicNamedAndTypedDecl( |
| abi_dump::BasicNamedAndTypedDecl *basic_named_and_typed_decl, |
| const clang::QualType type, const std::string &name, |
| const clang::AccessSpecifier &access, std::string key) const { |
| if (!basic_named_and_typed_decl) { |
| return false; |
| } |
| abi_dump::AccessSpecifier access_dump = AccessClangToDump(access); |
| basic_named_and_typed_decl->set_name(name); |
| basic_named_and_typed_decl->set_access(access_dump); |
| if (key != "") { |
| basic_named_and_typed_decl->set_linker_set_key(key); |
| } |
| return SetupBasicTypeAbi(basic_named_and_typed_decl->mutable_type_abi(), |
| type); |
| } |
| |
| std::string ABIWrapper::GetMangledNameDecl(const clang::NamedDecl *decl) const { |
| assert(&(mangle_contextp_->getASTContext()) == ast_contextp_); |
| if (!mangle_contextp_->shouldMangleDeclName(decl)) { |
| clang::IdentifierInfo *identifier = decl->getIdentifier(); |
| return identifier ? identifier->getName() : ""; |
| } |
| std::string mangled_name; |
| llvm::raw_string_ostream ostream(mangled_name); |
| mangle_contextp_->mangleName(decl, ostream); |
| ostream.flush(); |
| return mangled_name; |
| } |
| |
| bool ABIWrapper::SetupTemplateParamNames( |
| abi_dump::TemplateInfo *tinfo, |
| clang::TemplateParameterList *pl) const { |
| if (tinfo->elements_size() > 0) { |
| return true; |
| } |
| |
| clang::TemplateParameterList::iterator template_it = pl->begin(); |
| while (template_it != pl->end()) { |
| abi_dump::TemplateElement *template_parameterp = |
| tinfo->add_elements(); |
| if (!template_parameterp) { |
| return false; |
| } |
| abi_dump::TemplateElement::BasicTemplateElementAbi *basic_abi = |
| template_parameterp->mutable_basic_abi(); |
| if (!basic_abi) { |
| return false; |
| } |
| std::string name = (*template_it)->getName(); |
| basic_abi->set_name(name); |
| // TODO : Default arg ? |
| basic_abi->set_linker_set_key(name); |
| template_it++; |
| } |
| return true; |
| } |
| |
| std::string ABIWrapper::GetTagDeclQualifiedName( |
| const clang::TagDecl *decl) const { |
| if (decl->getTypedefNameForAnonDecl()) { |
| return decl->getTypedefNameForAnonDecl()->getQualifiedNameAsString(); |
| } |
| return decl->getQualifiedNameAsString(); |
| } |
| |
| bool ABIWrapper::SetupTemplateArguments( |
| abi_dump::TemplateInfo *tinfo, |
| const clang::TemplateArgumentList *tl) const { |
| for (int i = 0; i < tl->size(); i++) { |
| const clang::TemplateArgument &arg = (*tl)[i]; |
| //TODO: More comprehensive checking needed. |
| if (arg.getKind() != clang::TemplateArgument::Type) { |
| continue; |
| } |
| clang::QualType type = arg.getAsType(); |
| abi_dump::TemplateElement *template_parameterp = |
| tinfo->add_elements(); |
| if (!template_parameterp) { |
| return false; |
| } |
| abi_dump::TemplateElement::BasicTemplateElementAbi *basic_abi = |
| template_parameterp->mutable_basic_abi(); |
| if (!basic_abi || !SetupBasicTypeAbi(basic_abi->mutable_type_abi(), type)) { |
| return false; |
| } |
| // TODO : default arg |
| basic_abi->set_linker_set_key(QualTypeToString(type)); |
| } |
| return true; |
| } |
| |
| std::string ABIWrapper::QualTypeToString( |
| const clang::QualType &sweet_qt) const { |
| const clang::QualType salty_qt = sweet_qt.getCanonicalType(); |
| return clang::TypeName::getFullyQualifiedName(salty_qt, *ast_contextp_); |
| } |
| |
| FunctionDeclWrapper::FunctionDeclWrapper( |
| clang::MangleContext *mangle_contextp, |
| clang::ASTContext *ast_contextp, |
| const clang::CompilerInstance *compiler_instance_p, |
| const clang::FunctionDecl *decl) |
| : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), |
| function_decl_(decl) { } |
| |
| bool FunctionDeclWrapper::SetupFunctionParameters( |
| abi_dump::FunctionDecl *functionp) const { |
| clang::FunctionDecl::param_const_iterator param_it = |
| function_decl_->param_begin(); |
| while (param_it != function_decl_->param_end()) { |
| abi_dump::ParamDecl *function_fieldp = functionp->add_parameters(); |
| if (!function_fieldp) { |
| llvm::errs() << "Couldn't add parameter to method. Aborting\n"; |
| return false; |
| } |
| // The linker set key is blank since that shows up in the mangled name. |
| bool has_default_arg = (*param_it)->hasDefaultArg(); |
| if (!SetupBasicNamedAndTypedDecl( |
| function_fieldp->mutable_basic_abi(), |
| (*param_it)->getType(), (*param_it)->getName(), |
| (*param_it)->getAccess(), has_default_arg ? "true" : "false")) { |
| return false; |
| } |
| function_fieldp->set_default_arg(has_default_arg); |
| param_it++; |
| } |
| return true; |
| } |
| |
| bool FunctionDeclWrapper::SetupFunction(abi_dump::FunctionDecl *functionp, |
| const std::string &source_file) const { |
| // Go through all the parameters in the method and add them to the fields. |
| // Also get the fully qualfied name and mangled name and store them. |
| std::string mangled_name = GetMangledNameDecl(function_decl_); |
| functionp->set_mangled_function_name(mangled_name); |
| functionp->set_source_file(source_file); |
| // Combine the function name and return type to form a NamedAndTypedDecl |
| return SetupBasicNamedAndTypedDecl( |
| functionp->mutable_basic_abi(), |
| function_decl_->getReturnType(), |
| function_decl_->getQualifiedNameAsString(), |
| function_decl_->getAccess(), mangled_name) && |
| SetupTemplateInfo(functionp) && SetupFunctionParameters(functionp); |
| } |
| |
| bool FunctionDeclWrapper::SetupTemplateInfo( |
| abi_dump::FunctionDecl *functionp) const { |
| switch (function_decl_->getTemplatedKind()) { |
| case clang::FunctionDecl::TK_FunctionTemplate: { |
| clang::FunctionTemplateDecl *template_decl = |
| function_decl_->getDescribedFunctionTemplate(); |
| if (template_decl) { |
| clang::TemplateParameterList *template_parameter_list = |
| template_decl->getTemplateParameters(); |
| if (template_parameter_list && |
| !SetupTemplateParamNames(functionp->mutable_template_info(), |
| template_parameter_list)) { |
| return false; |
| } |
| } |
| break; |
| } |
| case clang::FunctionDecl::TK_FunctionTemplateSpecialization: { |
| const clang::TemplateArgumentList *arg_list = |
| function_decl_->getTemplateSpecializationArgs(); |
| if (arg_list && |
| !SetupTemplateArguments(functionp->mutable_template_info(), |
| arg_list)) { |
| return false; |
| } |
| break; |
| } |
| default: { |
| break; |
| } |
| } |
| return true; |
| } |
| |
| std::unique_ptr<abi_dump::FunctionDecl> |
| FunctionDeclWrapper::GetFunctionDecl() const { |
| std::unique_ptr<abi_dump::FunctionDecl> abi_decl( |
| new abi_dump::FunctionDecl()); |
| std::string source_file = GetDeclSourceFile(function_decl_, cip_); |
| if (!SetupFunction(abi_decl.get(), source_file)) { |
| return nullptr; |
| } |
| return abi_decl; |
| } |
| |
| RecordDeclWrapper::RecordDeclWrapper( |
| clang::MangleContext *mangle_contextp, |
| clang::ASTContext *ast_contextp, |
| const clang::CompilerInstance *compiler_instance_p, |
| const clang::RecordDecl *decl) |
| : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), |
| record_decl_(decl) { } |
| |
| bool RecordDeclWrapper::SetupRecordFields(abi_dump::RecordDecl *recordp) const { |
| clang::RecordDecl::field_iterator field = record_decl_->field_begin(); |
| while (field != record_decl_->field_end()) { |
| abi_dump::RecordFieldDecl *record_fieldp = recordp->add_fields(); |
| if (!record_fieldp) { |
| llvm::errs() << " Couldn't add record field: " << field->getName() |
| << " to reference dump\n"; |
| return false; |
| } |
| if (!SetupBasicNamedAndTypedDecl(record_fieldp->mutable_basic_abi(), |
| field->getType(), field->getName(), |
| field->getAccess(), "")) { |
| return false; |
| } |
| field++; |
| } |
| return true; |
| } |
| |
| bool RecordDeclWrapper::SetupCXXBases( |
| abi_dump::RecordDecl *cxxp, |
| const clang::CXXRecordDecl *cxx_record_decl) const { |
| assert(cxx_record_decl != nullptr); |
| clang::CXXRecordDecl::base_class_const_iterator base_class = |
| cxx_record_decl->bases_begin(); |
| while (base_class != cxx_record_decl->bases_end()) { |
| abi_dump::CXXBaseSpecifier *base_specifierp = cxxp->add_base_specifiers(); |
| if (!base_specifierp) { |
| llvm::errs() << " Couldn't add base specifier to reference dump\n"; |
| return false; |
| } |
| std::string name = QualTypeToString(base_class->getType()); |
| bool is_virtual = base_class->isVirtual(); |
| if (!SetupBasicNamedAndTypedDecl(base_specifierp->mutable_basic_abi(), |
| base_class->getType(), |
| "", base_class->getAccessSpecifier(), |
| "")) { |
| return false; |
| } |
| base_specifierp->set_is_virtual(is_virtual); |
| base_class++; |
| } |
| return true; |
| } |
| |
| bool RecordDeclWrapper::SetupRecordVTable( |
| abi_dump::RecordDecl *record_declp, |
| const clang::CXXRecordDecl *cxx_record_decl) const { |
| assert(cxx_record_decl != nullptr); |
| clang::VTableContextBase *base_vtable_contextp = |
| ast_contextp_->getVTableContext(); |
| const clang::Type *typep = cxx_record_decl->getTypeForDecl(); |
| if (!base_vtable_contextp || !typep) { |
| return false; |
| } |
| // Skip Microsoft ABI. |
| clang::ItaniumVTableContext *itanium_vtable_contextp = |
| llvm::dyn_cast<clang::ItaniumVTableContext>(base_vtable_contextp); |
| if (!itanium_vtable_contextp || !cxx_record_decl->isPolymorphic() || |
| typep->isDependentType() || typep->isIncompleteType()) { |
| return true; |
| } |
| const clang::VTableLayout &vtable_layout = |
| itanium_vtable_contextp->getVTableLayout(cxx_record_decl); |
| abi_dump::VTableLayout *vtablep = record_declp->mutable_vtable_layout(); |
| if (!vtablep) { |
| return false; |
| } |
| for (const auto &vtable_component : vtable_layout.vtable_components()) { |
| abi_dump::VTableComponent *added_vtable_component = |
| vtablep->add_vtable_components(); |
| if (!added_vtable_component || |
| !SetupRecordVTableComponent(added_vtable_component, vtable_component)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool RecordDeclWrapper::SetupRecordVTableComponent( |
| abi_dump::VTableComponent *added_vtable_component, |
| const clang::VTableComponent &vtable_component) const { |
| assert(added_vtable_component != nullptr); |
| abi_dump::VTableComponent_Kind kind = abi_dump::VTableComponent_Kind_RTTI; |
| std::string mangled_component_name = ""; |
| uint64_t value = 0; |
| clang::VTableComponent::Kind clang_component_kind = |
| vtable_component.getKind(); |
| switch (clang_component_kind) { |
| case clang::VTableComponent::CK_VCallOffset: |
| kind = abi_dump::VTableComponent_Kind_VCallOffset; |
| value = vtable_component.getVCallOffset().getQuantity(); |
| break; |
| case clang::VTableComponent::CK_VBaseOffset: |
| kind = abi_dump::VTableComponent_Kind_VBaseOffset; |
| value = vtable_component.getVBaseOffset().getQuantity(); |
| break; |
| case clang::VTableComponent::CK_OffsetToTop: |
| kind = abi_dump::VTableComponent_Kind_OffsetToTop; |
| value = vtable_component.getOffsetToTop().getQuantity(); |
| break; |
| case clang::VTableComponent::CK_RTTI: |
| { |
| kind = abi_dump::VTableComponent_Kind_RTTI; |
| const clang::CXXRecordDecl *rtti_decl = |
| vtable_component.getRTTIDecl(); |
| assert(rtti_decl != nullptr); |
| mangled_component_name = GetMangledNameDecl(rtti_decl); |
| } |
| break; |
| case clang::VTableComponent::CK_FunctionPointer: |
| case clang::VTableComponent::CK_CompleteDtorPointer: |
| case clang::VTableComponent::CK_DeletingDtorPointer: |
| case clang::VTableComponent::CK_UnusedFunctionPointer: |
| { |
| const clang::CXXMethodDecl *method_decl = |
| vtable_component.getFunctionDecl(); |
| assert(method_decl != nullptr); |
| mangled_component_name = GetMangledNameDecl(method_decl); |
| switch (clang_component_kind) { |
| case clang::VTableComponent::CK_FunctionPointer: |
| kind = abi_dump::VTableComponent_Kind_FunctionPointer; |
| break; |
| case clang::VTableComponent::CK_CompleteDtorPointer: |
| kind = abi_dump::VTableComponent_Kind_CompleteDtorPointer; |
| break; |
| case clang::VTableComponent::CK_DeletingDtorPointer: |
| kind = abi_dump::VTableComponent_Kind_DeletingDtorPointer; |
| case clang::VTableComponent::CK_UnusedFunctionPointer: |
| kind = abi_dump::VTableComponent_Kind_UnusedFunctionPointer; |
| default: |
| break; |
| } |
| } |
| break; |
| default: |
| return false; |
| } |
| added_vtable_component->set_kind(kind); |
| added_vtable_component->set_value(value); |
| added_vtable_component->set_mangled_component_name(mangled_component_name); |
| return true; |
| } |
| |
| bool RecordDeclWrapper::SetupTemplateInfo( |
| abi_dump::RecordDecl *record_declp, |
| const clang::CXXRecordDecl *cxx_record_decl) const { |
| assert(cxx_record_decl != nullptr); |
| if (cxx_record_decl->isTemplateDecl()) { |
| clang::ClassTemplateDecl *template_decl = |
| cxx_record_decl->getDescribedClassTemplate(); |
| if (template_decl) { |
| clang::TemplateParameterList *template_parameter_list = |
| template_decl->getTemplateParameters(); |
| if (template_parameter_list && |
| !SetupTemplateParamNames(record_declp->mutable_template_info(), |
| template_parameter_list)) { |
| return false; |
| } |
| } |
| } else { |
| const clang::ClassTemplateSpecializationDecl *specialization_decl = |
| clang::dyn_cast<clang::ClassTemplateSpecializationDecl>( |
| cxx_record_decl); |
| if(specialization_decl) { |
| const clang::TemplateArgumentList *arg_list = |
| &specialization_decl->getTemplateArgs(); |
| if (arg_list && |
| !SetupTemplateArguments(record_declp->mutable_template_info(), |
| arg_list)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool RecordDeclWrapper::SetupRecordInfo(abi_dump::RecordDecl *record_declp, |
| const std::string &source_file) const { |
| std::string qualified_name = GetTagDeclQualifiedName(record_decl_); |
| std::string mangled_name = GetMangledNameDecl(record_decl_); |
| const clang::Type *basic_type = nullptr; |
| if (!(basic_type = record_decl_->getTypeForDecl())) { |
| return false; |
| } |
| clang::QualType type = basic_type->getCanonicalTypeInternal(); |
| std::string linker_key = (mangled_name == "") ? qualified_name : mangled_name; |
| if (!SetupBasicNamedAndTypedDecl(record_declp->mutable_basic_abi(), |
| type, qualified_name, |
| record_decl_->getAccess(), linker_key)) { |
| return false; |
| } |
| record_declp->set_mangled_record_name(mangled_name); |
| record_declp->set_source_file(source_file); |
| return true; |
| } |
| |
| bool RecordDeclWrapper::SetupCXXRecordInfo( |
| abi_dump::RecordDecl *record_declp) const { |
| const clang::CXXRecordDecl *cxx_record_decl = |
| clang::dyn_cast<clang::CXXRecordDecl>(record_decl_); |
| if (!cxx_record_decl) { |
| return true; |
| } |
| return SetupTemplateInfo(record_declp, cxx_record_decl) && |
| SetupCXXBases(record_declp, cxx_record_decl) && |
| SetupRecordVTable(record_declp, cxx_record_decl); |
| } |
| |
| std::unique_ptr<abi_dump::RecordDecl> RecordDeclWrapper::GetRecordDecl() const { |
| std::unique_ptr<abi_dump::RecordDecl> abi_decl(new abi_dump::RecordDecl()); |
| std::string source_file = GetDeclSourceFile(record_decl_, cip_); |
| abi_dump::RecordDecl *record_declp = abi_decl.get(); |
| if (!SetupRecordInfo(record_declp, source_file) || |
| !SetupRecordFields(record_declp) || |
| !SetupCXXRecordInfo(abi_decl.get())) { |
| llvm::errs() << "Setting up CXX Bases / Template Info failed\n"; |
| return nullptr; |
| } |
| return abi_decl; |
| } |
| |
| EnumDeclWrapper::EnumDeclWrapper( |
| clang::MangleContext *mangle_contextp, |
| clang::ASTContext *ast_contextp, |
| const clang::CompilerInstance *compiler_instance_p, |
| const clang::EnumDecl *decl) |
| : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), |
| enum_decl_(decl) { } |
| |
| bool EnumDeclWrapper::SetupEnumFields(abi_dump::EnumDecl *enump) const { |
| clang::EnumDecl::enumerator_iterator enum_it = enum_decl_->enumerator_begin(); |
| while (enum_it != enum_decl_->enumerator_end()) { |
| abi_dump::EnumFieldDecl *enum_fieldp = enump->add_enum_fields(); |
| std::string name = enum_it->getQualifiedNameAsString(); |
| uint64_t field_value = enum_it->getInitVal().getExtValue(); |
| if (!enum_fieldp || |
| !SetupBasicNamedAndTypedDecl(enum_fieldp->mutable_basic_abi(), |
| enum_it->getType(), name, |
| enum_it->getAccess(), |
| std::to_string(field_value))) { |
| return false; |
| } |
| enum_fieldp->set_enum_field_value(field_value); |
| enum_it++; |
| } |
| return true; |
| } |
| |
| bool EnumDeclWrapper::SetupEnum(abi_dump::EnumDecl *enump, |
| const std::string &source_file) const { |
| std::string enum_name = GetTagDeclQualifiedName(enum_decl_); |
| std::string enum_mangled_name = GetMangledNameDecl(enum_decl_); |
| clang::QualType enum_type = enum_decl_->getIntegerType(); |
| if (!SetupBasicNamedAndTypedDecl(enump->mutable_basic_abi(), enum_type, |
| enum_name, enum_decl_->getAccess(), |
| enum_mangled_name) || |
| !SetupEnumFields(enump)) { |
| return false; |
| } |
| enump->set_source_file(source_file); |
| return true; |
| } |
| |
| std::unique_ptr<abi_dump::EnumDecl> EnumDeclWrapper::GetEnumDecl() const { |
| std::unique_ptr<abi_dump::EnumDecl> abi_decl(new abi_dump::EnumDecl()); |
| std::string source_file = GetDeclSourceFile(enum_decl_, cip_); |
| |
| if (!SetupEnum(abi_decl.get(), source_file)) { |
| llvm::errs() << "Setting up Enum fields failed\n"; |
| return nullptr; |
| } |
| return abi_decl; |
| } |
| |
| GlobalVarDeclWrapper::GlobalVarDeclWrapper( |
| clang::MangleContext *mangle_contextp, |
| clang::ASTContext *ast_contextp, |
| const clang::CompilerInstance *compiler_instance_p, |
| const clang::VarDecl *decl) |
| : ABIWrapper(mangle_contextp, ast_contextp, compiler_instance_p), |
| global_var_decl_(decl) { } |
| |
| bool GlobalVarDeclWrapper::SetupGlobalVar( |
| abi_dump::GlobalVarDecl *global_varp, |
| const std::string &source_file) const { |
| // Temporary fix : clang segfaults on trying to mangle global variable which |
| // is a dependent sized array type. |
| std::string qualified_name = global_var_decl_->getQualifiedNameAsString(); |
| std::string mangled_or_qualified_name = |
| global_var_decl_->getType()->isDependentSizedArrayType() ? |
| qualified_name : GetMangledNameDecl(global_var_decl_); |
| if (!SetupBasicNamedAndTypedDecl( |
| global_varp->mutable_basic_abi(),global_var_decl_->getType(), |
| qualified_name, global_var_decl_->getAccess(), |
| mangled_or_qualified_name)) { |
| return false; |
| } |
| global_varp->set_source_file(source_file); |
| return true; |
| } |
| |
| std::unique_ptr<abi_dump::GlobalVarDecl> |
| GlobalVarDeclWrapper::GetGlobalVarDecl() const { |
| std::unique_ptr<abi_dump::GlobalVarDecl> |
| abi_decl(new abi_dump::GlobalVarDecl); |
| std::string source_file = GetDeclSourceFile(global_var_decl_, cip_); |
| if (!SetupGlobalVar(abi_decl.get(), source_file)) { |
| return nullptr; |
| } |
| return abi_decl; |
| } |