mirror of https://github.com/mono/CppSharp.git
c-sharpdotnetmonobindingsbridgecclangcpluspluscppsharpglueinteropparserparsingpinvokeswigsyntax-treevisitorsxamarinxamarin-bindings
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2230 lines
64 KiB
2230 lines
64 KiB
/************************************************************************ |
|
* |
|
* CppSharp |
|
* Licensed under the simplified BSD license. All rights reserved. |
|
* |
|
************************************************************************/ |
|
|
|
#include "Parser.h" |
|
#include "Interop.h" |
|
|
|
#include <llvm/Support/Path.h> |
|
#include <llvm/Object/Archive.h> |
|
#include <llvm/Object/ObjectFile.h> |
|
#include <clang/Basic/Version.h> |
|
#include <clang/Config/config.h> |
|
#include <clang/AST/ASTContext.h> |
|
#include <clang/AST/Comment.h> |
|
#include <clang/AST/DeclTemplate.h> |
|
#include <clang/Lex/DirectoryLookup.h> |
|
#include <clang/Lex/HeaderSearch.h> |
|
#include <clang/Lex/PreprocessingRecord.h> |
|
#include <clang/Sema/Sema.h> |
|
#include <clang/Sema/SemaConsumer.h> |
|
#include <clang/Frontend/Utils.h> |
|
#include <clang/Driver/Util.h> |
|
|
|
#include <string> |
|
#include <iostream> |
|
#include <sstream> |
|
|
|
//-----------------------------------// |
|
|
|
Parser::Parser(ParserOptions^ Opts) : Lib(Opts->Library), Opts(Opts), Index(0) |
|
{ |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
static std::string GetClangResourceDir(const std::string& Dir) |
|
{ |
|
using namespace llvm; |
|
using namespace clang; |
|
|
|
// Compute the path to the resource directory. |
|
StringRef ClangResourceDir(CLANG_RESOURCE_DIR); |
|
|
|
SmallString<128> P(Dir); |
|
|
|
if (ClangResourceDir != "") |
|
llvm::sys::path::append(P, ClangResourceDir); |
|
else |
|
llvm::sys::path::append(P, "lib", "clang", CLANG_VERSION_STRING); |
|
|
|
return P.str(); |
|
} |
|
|
|
static std::string GetClangBuiltinIncludeDir() |
|
{ |
|
using namespace llvm; |
|
|
|
SmallString<128> P( GetClangResourceDir(".") ); |
|
llvm::sys::path::append(P, "include"); |
|
|
|
return P.str(); |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
#ifdef _MSC_VER |
|
std::vector<std::string> GetWindowsSystemIncludeDirs(); |
|
#endif |
|
|
|
void Parser::SetupHeader() |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
std::vector<const char*> args; |
|
args.push_back("-cc1"); |
|
|
|
// Enable C++ language mode |
|
args.push_back("-xc++"); |
|
args.push_back("-std=gnu++11"); |
|
//args.push_back("-Wno-undefined-inline"); |
|
args.push_back("-fno-rtti"); |
|
|
|
// Enable the Microsoft parsing extensions |
|
if (Opts->MicrosoftMode) |
|
{ |
|
args.push_back("-fms-extensions"); |
|
args.push_back("-fms-compatibility"); |
|
args.push_back("-fdelayed-template-parsing"); |
|
} |
|
|
|
C.reset(new CompilerInstance()); |
|
C->createDiagnostics(); |
|
|
|
CompilerInvocation* Inv = new CompilerInvocation(); |
|
CompilerInvocation::CreateFromArgs(*Inv, args.data(), args.data() + args.size(), |
|
C->getDiagnostics()); |
|
C->setInvocation(Inv); |
|
|
|
TargetOptions& TO = Inv->getTargetOpts(); |
|
if (!System::String::IsNullOrWhiteSpace(Opts->TargetTriple)) |
|
TO.Triple = marshalString<E_UTF8>(Opts->TargetTriple); |
|
else |
|
TO.Triple = llvm::sys::getDefaultTargetTriple(); |
|
|
|
TargetABI = Opts->MicrosoftMode ? TargetCXXABI::Microsoft |
|
: TargetCXXABI::GenericItanium; |
|
|
|
TargetInfo* TI = TargetInfo::CreateTargetInfo(C->getDiagnostics(), &TO); |
|
TI->setCXXABI(TargetABI); |
|
C->setTarget(TI); |
|
|
|
C->createFileManager(); |
|
C->createSourceManager(C->getFileManager()); |
|
|
|
if (Opts->NoStandardIncludes) |
|
{ |
|
auto HSOpts = C->getHeaderSearchOpts(); |
|
HSOpts.UseStandardSystemIncludes = false; |
|
HSOpts.UseStandardCXXIncludes = false; |
|
} |
|
|
|
if (Opts->NoBuiltinIncludes) |
|
{ |
|
auto HSOpts = C->getHeaderSearchOpts(); |
|
HSOpts.UseBuiltinIncludes = false; |
|
} |
|
|
|
if (Opts->Verbose) |
|
C->getHeaderSearchOpts().Verbose = true; |
|
|
|
for each(System::String^% include in Opts->IncludeDirs) |
|
{ |
|
String s = marshalString<E_UTF8>(include); |
|
C->getHeaderSearchOpts().AddPath(s, frontend::Angled, false, false); |
|
} |
|
|
|
for each(System::String^% include in Opts->SystemIncludeDirs) |
|
{ |
|
String s = marshalString<E_UTF8>(include); |
|
C->getHeaderSearchOpts().AddPath(s, frontend::System, false, false); |
|
} |
|
|
|
for each(System::String^% def in Opts->Defines) |
|
{ |
|
String s = marshalString<E_UTF8>(def); |
|
C->getPreprocessorOpts().addMacroDef(s); |
|
} |
|
|
|
// Initialize the default platform headers. |
|
std::string ResourceDir = GetClangResourceDir("."); |
|
C->getHeaderSearchOpts().ResourceDir = ResourceDir; |
|
C->getHeaderSearchOpts().AddPath(GetClangBuiltinIncludeDir(), |
|
clang::frontend::System, false, false); |
|
|
|
#ifdef _MSC_VER |
|
if (!Opts->NoBuiltinIncludes) |
|
{ |
|
std::vector<std::string> SystemDirs = GetWindowsSystemIncludeDirs(); |
|
clang::HeaderSearchOptions& HSOpts = C->getHeaderSearchOpts(); |
|
|
|
for(size_t i = 0; i < SystemDirs.size(); ++i) |
|
{ |
|
HSOpts.AddPath(SystemDirs[i], frontend::System, false, false); |
|
} |
|
} |
|
#endif |
|
|
|
C->createPreprocessor(); |
|
C->createASTContext(); |
|
|
|
if (C->hasPreprocessor()) |
|
{ |
|
Preprocessor& P = C->getPreprocessor(); |
|
P.createPreprocessingRecord(); |
|
P.getBuiltinInfo().InitializeBuiltins(P.getIdentifierTable(), |
|
P.getLangOpts()); |
|
} |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
std::string Parser::GetDeclMangledName(clang::Decl* D, clang::TargetCXXABI ABI, |
|
bool IsDependent) |
|
{ |
|
using namespace clang; |
|
|
|
if(!D || !isa<NamedDecl>(D)) |
|
return ""; |
|
|
|
bool CanMangle = isa<FunctionDecl>(D) || isa<VarDecl>(D) |
|
|| isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D); |
|
|
|
if (!CanMangle) return ""; |
|
|
|
NamedDecl* ND = cast<NamedDecl>(D); |
|
llvm::OwningPtr<MangleContext> MC; |
|
|
|
switch(ABI.getKind()) |
|
{ |
|
default: |
|
llvm_unreachable("Unknown mangling ABI"); |
|
break; |
|
case TargetCXXABI::GenericItanium: |
|
MC.reset(createItaniumMangleContext(*AST, AST->getDiagnostics())); |
|
//AST->setCXXABI(CreateItaniumCXXABI(*AST)); |
|
break; |
|
case TargetCXXABI::Microsoft: |
|
MC.reset(createMicrosoftMangleContext(*AST, AST->getDiagnostics())); |
|
//AST->setCXXABI(CreateMicrosoftCXXABI(*AST)); |
|
break; |
|
} |
|
|
|
std::string Mangled; |
|
llvm::raw_string_ostream Out(Mangled); |
|
|
|
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ND)) |
|
IsDependent = VD->getType()->isDependentType(); |
|
|
|
if (!MC->shouldMangleDeclName(ND) || IsDependent) |
|
return ND->getDeclName().getAsString(); |
|
|
|
if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(ND)) |
|
MC->mangleCXXCtor(CD, Ctor_Base, Out); |
|
else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(ND)) |
|
MC->mangleCXXDtor(DD, Dtor_Base, Out); |
|
else |
|
MC->mangleName(ND, Out); |
|
|
|
Out.flush(); |
|
|
|
// Strip away LLVM name marker. |
|
if(!Mangled.empty() && Mangled[0] == '\01') |
|
Mangled = Mangled.substr(1); |
|
|
|
return Mangled; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
static std::string GetDeclName(const clang::NamedDecl* D) |
|
{ |
|
if (const clang::IdentifierInfo *II = D->getIdentifier()) |
|
return II->getName(); |
|
return D->getNameAsString(); |
|
} |
|
|
|
static std::string GetTagDeclName(const clang::TagDecl* D) |
|
{ |
|
using namespace clang; |
|
|
|
if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) |
|
{ |
|
assert(Typedef->getIdentifier() && "Typedef without identifier?"); |
|
return GetDeclName(Typedef); |
|
} |
|
|
|
return GetDeclName(D); |
|
} |
|
|
|
static clang::Decl* GetPreviousDeclInContext(const clang::Decl* D) |
|
{ |
|
assert(!D->getLexicalDeclContext()->decls_empty()); |
|
|
|
clang::Decl* prevDecl = nullptr; |
|
for(auto it = D->getDeclContext()->decls_begin(); |
|
it != D->getDeclContext()->decls_end(); it++) |
|
{ |
|
if((*it) == D) |
|
return prevDecl; |
|
prevDecl = (*it); |
|
} |
|
|
|
return nullptr; |
|
} |
|
|
|
static clang::SourceLocation GetDeclStartLocation(clang::CompilerInstance* C, |
|
const clang::Decl* D) |
|
{ |
|
auto &SM = C->getSourceManager(); |
|
auto startLoc = SM.getExpansionLoc(D->getLocStart()); |
|
auto startOffset = SM.getFileOffset(startLoc); |
|
|
|
auto lineNo = SM.getExpansionLineNumber(startLoc); |
|
auto lineBeginLoc = SM.translateLineCol(SM.getFileID(startLoc), lineNo, 1); |
|
auto lineBeginOffset = SM.getFileOffset(lineBeginLoc); |
|
assert(lineBeginOffset <= startOffset); |
|
|
|
auto prevDecl = GetPreviousDeclInContext(D); |
|
if(!prevDecl) |
|
return lineBeginLoc; |
|
|
|
auto prevDeclEndLoc = SM.getExpansionLoc(prevDecl->getLocEnd()); |
|
auto prevDeclEndOffset = SM.getFileOffset(prevDeclEndLoc); |
|
|
|
if(SM.getFileID(prevDeclEndLoc) != SM.getFileID(startLoc)) |
|
return lineBeginLoc; |
|
|
|
// TODO: Figure out why this asserts |
|
//assert(prevDeclEndOffset <= startOffset); |
|
|
|
if(prevDeclEndOffset < lineBeginOffset) |
|
return lineBeginLoc; |
|
|
|
// Declarations don't share same macro expansion |
|
if(SM.getExpansionLoc(prevDecl->getLocStart()) != startLoc) |
|
return prevDeclEndLoc; |
|
|
|
return GetDeclStartLocation(C, prevDecl); |
|
} |
|
|
|
std::string Parser::GetTypeName(const clang::Type* Type) |
|
{ |
|
using namespace clang; |
|
|
|
if(Type->isAnyPointerType() || Type->isReferenceType()) |
|
Type = Type->getPointeeType().getTypePtr(); |
|
|
|
if(Type->isEnumeralType() || Type->isRecordType()) |
|
{ |
|
const clang::TagType* Tag = Type->getAs<clang::TagType>(); |
|
return GetTagDeclName(Tag->getDecl()); |
|
} |
|
|
|
PrintingPolicy pp(C->getLangOpts()); |
|
pp.SuppressTagKeyword = true; |
|
|
|
std::string TypeName; |
|
QualType::getAsStringInternal(Type, Qualifiers(), TypeName, pp); |
|
|
|
return TypeName; |
|
} |
|
|
|
static CppSharp::AST::TypeQualifiers GetTypeQualifiers(clang::QualType Type) |
|
{ |
|
CppSharp::AST::TypeQualifiers quals; |
|
quals.IsConst = Type.isLocalConstQualified(); |
|
quals.IsRestrict = Type.isLocalRestrictQualified(); |
|
quals.IsVolatile = Type.isVolatileQualified(); |
|
return quals; |
|
} |
|
|
|
static CppSharp::AST::QualifiedType |
|
GetQualifiedType(clang::QualType qual, CppSharp::AST::Type^ type) |
|
{ |
|
CppSharp::AST::QualifiedType qualType; |
|
qualType.Type = type; |
|
qualType.Qualifiers = GetTypeQualifiers(qual); |
|
return qualType; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
static CppSharp::AST::AccessSpecifier ConvertToAccess(clang::AccessSpecifier AS) |
|
{ |
|
switch(AS) |
|
{ |
|
case clang::AS_private: |
|
return CppSharp::AST::AccessSpecifier::Private; |
|
case clang::AS_protected: |
|
return CppSharp::AST::AccessSpecifier::Protected; |
|
case clang::AS_public: |
|
return CppSharp::AST::AccessSpecifier::Public; |
|
} |
|
|
|
return CppSharp::AST::AccessSpecifier::Public; |
|
} |
|
|
|
CppSharp::AST::VTableComponent |
|
Parser::WalkVTableComponent(const clang::VTableComponent& Component) |
|
{ |
|
using namespace clang; |
|
CppSharp::AST::VTableComponent VTC; |
|
|
|
switch(Component.getKind()) |
|
{ |
|
case VTableComponent::CK_VCallOffset: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::VBaseOffset; |
|
VTC.Offset = Component.getVCallOffset().getQuantity(); |
|
break; |
|
} |
|
case VTableComponent::CK_VBaseOffset: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::VBaseOffset; |
|
VTC.Offset = Component.getVBaseOffset().getQuantity(); |
|
break; |
|
} |
|
case VTableComponent::CK_OffsetToTop: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::OffsetToTop; |
|
VTC.Offset = Component.getOffsetToTop().getQuantity(); |
|
break; |
|
} |
|
case VTableComponent::CK_RTTI: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::RTTI; |
|
auto RD = const_cast<CXXRecordDecl*>(Component.getRTTIDecl()); |
|
VTC.Declaration = WalkRecordCXX(RD); |
|
break; |
|
} |
|
case VTableComponent::CK_FunctionPointer: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::FunctionPointer; |
|
auto MD = const_cast<CXXMethodDecl*>(Component.getFunctionDecl()); |
|
VTC.Declaration = WalkMethodCXX(MD); |
|
break; |
|
} |
|
case VTableComponent::CK_CompleteDtorPointer: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::CompleteDtorPointer; |
|
auto MD = const_cast<CXXDestructorDecl*>(Component.getDestructorDecl()); |
|
VTC.Declaration = WalkMethodCXX(MD); |
|
break; |
|
} |
|
case VTableComponent::CK_DeletingDtorPointer: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::DeletingDtorPointer; |
|
auto MD = const_cast<CXXDestructorDecl*>(Component.getDestructorDecl()); |
|
VTC.Declaration = WalkMethodCXX(MD); |
|
break; |
|
} |
|
case VTableComponent::CK_UnusedFunctionPointer: |
|
{ |
|
VTC.Kind = CppSharp::AST::VTableComponentKind::UnusedFunctionPointer; |
|
auto MD = const_cast<CXXMethodDecl*>(Component.getUnusedFunctionDecl()); |
|
VTC.Declaration = WalkMethodCXX(MD); |
|
break; |
|
} |
|
default: |
|
llvm_unreachable("Unknown vtable component kind"); |
|
} |
|
|
|
return VTC; |
|
} |
|
|
|
CppSharp::AST::VTableLayout^ |
|
Parser::WalkVTableLayout(const clang::VTableLayout& VTLayout) |
|
{ |
|
auto Layout = gcnew CppSharp::AST::VTableLayout(); |
|
|
|
for (auto I = VTLayout.vtable_component_begin(), |
|
E = VTLayout.vtable_component_end(); I != E; ++I) |
|
{ |
|
auto VTComponent = WalkVTableComponent(*I); |
|
Layout->Components->Add(VTComponent); |
|
} |
|
|
|
return Layout; |
|
} |
|
|
|
|
|
void Parser::WalkVTable(clang::CXXRecordDecl* RD, CppSharp::AST::Class^ C) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
assert(RD->isDynamicClass() && "Only dynamic classes have virtual tables"); |
|
|
|
switch(TargetABI) |
|
{ |
|
case TargetCXXABI::Microsoft: |
|
{ |
|
#if SVN_REVISION >= 187409 |
|
MicrosoftVFTableContext VTContext(*AST); |
|
|
|
auto VFPtrs = VTContext.getVFPtrOffsets(RD); |
|
for (auto I = VFPtrs.begin(), E = VFPtrs.end(); I != E; ++I) |
|
{ |
|
auto& VFPtrInfo = *I; |
|
|
|
CppSharp::AST::VFTableInfo Info; |
|
Info.VBTableIndex = VFPtrInfo.VBTableIndex; |
|
Info.VFPtrOffset = VFPtrInfo.VFPtrOffset.getQuantity(); |
|
Info.VFPtrFullOffset = VFPtrInfo.VFPtrFullOffset.getQuantity(); |
|
|
|
auto& VTLayout = VTContext.getVFTableLayout(RD, VFPtrInfo.VFPtrOffset); |
|
Info.Layout = WalkVTableLayout(VTLayout); |
|
|
|
C->Layout->VFTables->Add(Info); |
|
break; |
|
} |
|
#endif |
|
} |
|
case TargetCXXABI::GenericItanium: |
|
{ |
|
VTableContext VTContext(*AST); |
|
|
|
auto& VTLayout = VTContext.getVTableLayout(RD); |
|
C->Layout->Layout = WalkVTableLayout(VTLayout); |
|
break; |
|
} |
|
default: |
|
llvm_unreachable("Unsupported C++ ABI kind"); |
|
} |
|
} |
|
|
|
CppSharp::AST::Class^ Parser::WalkRecordCXX(clang::CXXRecordDecl* Record) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
if (Record->isInjectedClassName()) |
|
return nullptr; |
|
|
|
auto NS = GetNamespace(Record); |
|
assert(NS && "Expected a valid namespace"); |
|
|
|
bool isCompleteDefinition = Record->isCompleteDefinition(); |
|
|
|
CppSharp::AST::Class^ RC = nullptr; |
|
|
|
auto Name = marshalString<E_UTF8>(GetTagDeclName(Record)); |
|
auto HasEmptyName = Record->getDeclName().isEmpty(); |
|
|
|
if (HasEmptyName) |
|
{ |
|
if (auto AR = NS->FindAnonymous((uint64_t)Record)) |
|
RC = safe_cast<CppSharp::AST::Class^>(AR); |
|
} |
|
else |
|
{ |
|
RC = NS->FindClass(Name, isCompleteDefinition, /*Create=*/false); |
|
} |
|
|
|
if (RC) |
|
return RC; |
|
|
|
RC = NS->FindClass(Name, isCompleteDefinition, /*Create=*/true); |
|
|
|
if (HasEmptyName) |
|
NS->Anonymous[(uint64_t)Record] = RC; |
|
|
|
if (!isCompleteDefinition) |
|
return RC; |
|
|
|
auto headStartLoc = GetDeclStartLocation(C.get(), Record); |
|
auto headEndLoc = Record->getLocation(); // identifier location |
|
auto bodyEndLoc = Record->getLocEnd(); |
|
|
|
auto headRange = clang::SourceRange(headStartLoc, headEndLoc); |
|
auto bodyRange = clang::SourceRange(headEndLoc, bodyEndLoc); |
|
|
|
HandlePreprocessedEntities(RC, headRange, CppSharp::AST::MacroLocation::ClassHead); |
|
HandlePreprocessedEntities(RC, bodyRange, CppSharp::AST::MacroLocation::ClassBody); |
|
|
|
RC->IsPOD = Record->isPOD(); |
|
RC->IsUnion = Record->isUnion(); |
|
RC->IsAbstract = Record->isAbstract(); |
|
RC->IsDependent = Record->isDependentType(); |
|
RC->IsDynamic = Record->isDynamicClass(); |
|
RC->IsPolymorphic = Record->isPolymorphic(); |
|
|
|
auto &Sema = C->getSema(); |
|
Sema.ForceDeclarationOfImplicitMembers(Record); |
|
|
|
// Get the record layout information. |
|
const ASTRecordLayout* Layout = 0; |
|
if (!Record->isDependentType()) |
|
{ |
|
Layout = &C->getASTContext().getASTRecordLayout(Record); |
|
RC->Layout->Alignment = (int)Layout-> getAlignment().getQuantity(); |
|
RC->Layout->Size = (int)Layout->getSize().getQuantity(); |
|
RC->Layout->DataSize = (int)Layout->getDataSize().getQuantity(); |
|
RC->Layout->HasOwnVFPtr = Layout->hasOwnVFPtr(); |
|
RC->Layout->VBPtrOffset = Layout->getVBPtrOffset().getQuantity(); |
|
} |
|
|
|
CppSharp::AST::AccessSpecifierDecl^ AccessDecl = nullptr; |
|
|
|
for(auto it = Record->decls_begin(); it != Record->decls_end(); ++it) |
|
{ |
|
auto D = *it; |
|
|
|
switch(D->getKind()) |
|
{ |
|
case Decl::CXXConstructor: |
|
case Decl::CXXDestructor: |
|
case Decl::CXXConversion: |
|
case Decl::CXXMethod: |
|
{ |
|
auto MD = cast<CXXMethodDecl>(D); |
|
auto Method = WalkMethodCXX(MD); |
|
Method->AccessDecl = AccessDecl; |
|
RC->Methods->Add(Method); |
|
HandleComments(MD, Method); |
|
break; |
|
} |
|
case Decl::Field: |
|
{ |
|
auto FD = cast<FieldDecl>(D); |
|
auto Field = WalkFieldCXX(FD, RC); |
|
|
|
if (Layout) |
|
Field->Offset = Layout->getFieldOffset(FD->getFieldIndex()); |
|
|
|
RC->Fields->Add(Field); |
|
HandleComments(FD, Field); |
|
break; |
|
} |
|
case Decl::AccessSpec: |
|
{ |
|
AccessSpecDecl* AS = cast<AccessSpecDecl>(D); |
|
|
|
AccessDecl = gcnew CppSharp::AST::AccessSpecifierDecl(); |
|
AccessDecl->Access = ConvertToAccess(AS->getAccess()); |
|
AccessDecl->Namespace = RC; |
|
|
|
auto startLoc = GetDeclStartLocation(C.get(), AS); |
|
auto range = SourceRange(startLoc, AS->getColonLoc()); |
|
HandlePreprocessedEntities(AccessDecl, range, |
|
CppSharp::AST::MacroLocation::Unknown); |
|
|
|
RC->Specifiers->Add(AccessDecl); |
|
break; |
|
} |
|
case Decl::IndirectField: // FIXME: Handle indirect fields |
|
break; |
|
default: |
|
{ |
|
auto Decl = WalkDeclaration(D); |
|
break; |
|
} } |
|
} |
|
|
|
// Iterate through the record bases. |
|
for(auto it = Record->bases_begin(); it != Record->bases_end(); ++it) |
|
{ |
|
clang::CXXBaseSpecifier &BS = *it; |
|
|
|
CppSharp::AST::BaseClassSpecifier^ Base = gcnew CppSharp::AST::BaseClassSpecifier(); |
|
Base->Access = ConvertToAccess(BS.getAccessSpecifier()); |
|
Base->IsVirtual = BS.isVirtual(); |
|
Base->Type = WalkType(BS.getType(), &BS.getTypeSourceInfo()->getTypeLoc()); |
|
|
|
RC->Bases->Add(Base); |
|
} |
|
|
|
// Process the vtables |
|
if (Record->isDynamicClass()) |
|
WalkVTable(Record, RC); |
|
|
|
return RC; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::ClassTemplate^ Parser::WalkClassTemplate(clang::ClassTemplateDecl* TD) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
auto Class = WalkRecordCXX(TD->getTemplatedDecl()); |
|
CppSharp::AST::ClassTemplate^ CT = gcnew CppSharp::AST::ClassTemplate(Class); |
|
|
|
auto TPL = TD->getTemplateParameters(); |
|
for(auto it = TPL->begin(); it != TPL->end(); ++it) |
|
{ |
|
auto ND = *it; |
|
|
|
auto TP = CppSharp::AST::TemplateParameter(); |
|
TP.Name = clix::marshalString<clix::E_UTF8>(ND->getNameAsString()); |
|
|
|
CT->Parameters->Add(TP); |
|
} |
|
|
|
return CT; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::FunctionTemplate^ Parser::WalkFunctionTemplate(clang::FunctionTemplateDecl* TD) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
auto Function = WalkFunction(TD->getTemplatedDecl(), /*IsDependent=*/true, |
|
/*AddToNamespace=*/false); |
|
CppSharp::AST::FunctionTemplate^ FT = gcnew CppSharp::AST::FunctionTemplate(Function); |
|
|
|
auto TPL = TD->getTemplateParameters(); |
|
for(auto it = TPL->begin(); it != TPL->end(); ++it) |
|
{ |
|
auto ND = *it; |
|
|
|
auto TP = CppSharp::AST::TemplateParameter(); |
|
TP.Name = clix::marshalString<clix::E_UTF8>(ND->getNameAsString()); |
|
|
|
FT->Parameters->Add(TP); |
|
} |
|
|
|
return FT; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
static CppSharp::AST::CXXMethodKind GetMethodKindFromDecl(clang::DeclarationName Name) |
|
{ |
|
using namespace clang; |
|
|
|
switch(Name.getNameKind()) |
|
{ |
|
case DeclarationName::Identifier: |
|
case DeclarationName::ObjCZeroArgSelector: |
|
case DeclarationName::ObjCOneArgSelector: |
|
case DeclarationName::ObjCMultiArgSelector: |
|
return CppSharp::AST::CXXMethodKind::Normal; |
|
case DeclarationName::CXXConstructorName: |
|
return CppSharp::AST::CXXMethodKind::Constructor; |
|
case DeclarationName::CXXDestructorName: |
|
return CppSharp::AST::CXXMethodKind::Destructor; |
|
case DeclarationName::CXXConversionFunctionName: |
|
return CppSharp::AST::CXXMethodKind::Conversion; |
|
case DeclarationName::CXXOperatorName: |
|
case DeclarationName::CXXLiteralOperatorName: |
|
return CppSharp::AST::CXXMethodKind::Operator; |
|
case DeclarationName::CXXUsingDirective: |
|
return CppSharp::AST::CXXMethodKind::UsingDirective; |
|
} |
|
return CppSharp::AST::CXXMethodKind::Normal; |
|
} |
|
|
|
static CppSharp::AST::CXXOperatorKind GetOperatorKindFromDecl(clang::DeclarationName Name) |
|
{ |
|
using namespace clang; |
|
|
|
if (Name.getNameKind() != DeclarationName::CXXOperatorName) |
|
return CppSharp::AST::CXXOperatorKind::None; |
|
|
|
switch(Name.getCXXOverloadedOperator()) |
|
{ |
|
#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ |
|
case OO_##Name: return CppSharp::AST::CXXOperatorKind::Name; |
|
#include "clang/Basic/OperatorKinds.def" |
|
} |
|
|
|
return CppSharp::AST::CXXOperatorKind::None; |
|
} |
|
|
|
CppSharp::AST::Method^ Parser::WalkMethodCXX(clang::CXXMethodDecl* MD) |
|
{ |
|
using namespace clang; |
|
|
|
// We could be in a redeclaration, so process the primary context. |
|
if (MD->getPrimaryContext() != MD) |
|
return WalkMethodCXX(cast<CXXMethodDecl>(MD->getPrimaryContext())); |
|
|
|
auto RD = MD->getParent(); |
|
auto Class = WalkRecordCXX(RD); |
|
|
|
// Check for an already existing method that came from the same declaration. |
|
for each (CppSharp::AST::Method^ Method in Class->Methods) |
|
{ |
|
if (Method->OriginalPtr.ToPointer() == MD) |
|
return Method; |
|
} |
|
|
|
DeclarationName Name = MD->getDeclName(); |
|
|
|
CppSharp::AST::Method^ Method = gcnew CppSharp::AST::Method(); |
|
Method->Access = ConvertToAccess(MD->getAccess()); |
|
Method->Kind = GetMethodKindFromDecl(Name); |
|
Method->OperatorKind = GetOperatorKindFromDecl(Name); |
|
Method->IsStatic = MD->isStatic(); |
|
Method->IsVirtual = MD->isVirtual(); |
|
|
|
WalkFunction(MD, Method); |
|
|
|
if (const CXXConstructorDecl* CD = dyn_cast<CXXConstructorDecl>(MD)) |
|
{ |
|
Method->IsDefaultConstructor = CD->isDefaultConstructor(); |
|
Method->IsCopyConstructor = CD->isCopyConstructor(); |
|
Method->IsMoveConstructor = CD->isMoveConstructor(); |
|
} |
|
else if (const CXXDestructorDecl* DD = dyn_cast<CXXDestructorDecl>(MD)) |
|
{ |
|
} |
|
else if (const CXXConversionDecl* CD = dyn_cast<CXXConversionDecl>(MD)) |
|
{ |
|
} |
|
|
|
return Method; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::Field^ Parser::WalkFieldCXX(clang::FieldDecl* FD, CppSharp::AST::Class^ Class) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
CppSharp::AST::Field^ F = gcnew CppSharp::AST::Field(); |
|
F->Namespace = Class; |
|
|
|
F->Name = marshalString<E_UTF8>(FD->getName()); |
|
auto TL = FD->getTypeSourceInfo()->getTypeLoc(); |
|
F->QualifiedType = GetQualifiedType(FD->getType(), WalkType(FD->getType(), &TL)); |
|
F->Access = ConvertToAccess(FD->getAccess()); |
|
F->Class = Class; |
|
|
|
HandleComments(FD, F); |
|
|
|
return F; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::TranslationUnit^ Parser::GetTranslationUnit(clang::SourceLocation Loc, |
|
SourceLocationKind *Kind) |
|
{ |
|
using namespace clang; |
|
|
|
SourceManager& SM = C->getSourceManager(); |
|
|
|
if (Loc.isMacroID()) |
|
Loc = SM.getExpansionLoc(Loc); |
|
|
|
StringRef File; |
|
|
|
auto LocKind = GetLocationKind(Loc); |
|
switch(LocKind) |
|
{ |
|
case SourceLocationKind::Invalid: |
|
File = "<invalid>"; |
|
break; |
|
case SourceLocationKind::Builtin: |
|
File = "<built-in>"; |
|
break; |
|
case SourceLocationKind::CommandLine: |
|
File = "<command-line>"; |
|
break; |
|
default: |
|
File = SM.getFilename(Loc); |
|
assert(!File.empty() && "Expected to find a valid file"); |
|
break; |
|
} |
|
|
|
if (Kind) |
|
*Kind = LocKind; |
|
|
|
auto Unit = Lib->FindOrCreateModule(clix::marshalString<clix::E_UTF8>(File)); |
|
if (LocKind != SourceLocationKind::Invalid) |
|
Unit->IsSystemHeader = SM.isInSystemHeader(Loc); |
|
|
|
return Unit; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::TranslationUnit^ Parser::GetTranslationUnit(const clang::Decl* D) |
|
{ |
|
clang::SourceLocation Loc = D->getLocation(); |
|
|
|
SourceLocationKind Kind; |
|
CppSharp::AST::TranslationUnit^ Unit = GetTranslationUnit(Loc, &Kind); |
|
|
|
return Unit; |
|
} |
|
|
|
CppSharp::AST::DeclarationContext^ Parser::GetNamespace(clang::Decl* D, |
|
clang::DeclContext *Ctx) |
|
{ |
|
using namespace clang; |
|
|
|
// If the declaration is at global scope, just early exit. |
|
if (Ctx->isTranslationUnit()) |
|
return GetTranslationUnit(D); |
|
|
|
CppSharp::AST::TranslationUnit^ Unit = GetTranslationUnit(cast<Decl>(Ctx)); |
|
|
|
// Else we need to do a more expensive check to get all the namespaces, |
|
// and then perform a reverse iteration to get the namespaces in order. |
|
typedef SmallVector<DeclContext *, 8> ContextsTy; |
|
ContextsTy Contexts; |
|
|
|
for(; Ctx != nullptr; Ctx = Ctx->getParent()) |
|
Contexts.push_back(Ctx); |
|
|
|
assert(Contexts.back()->isTranslationUnit()); |
|
Contexts.pop_back(); |
|
|
|
CppSharp::AST::DeclarationContext^ DC = Unit; |
|
|
|
for (auto I = Contexts.rbegin(), E = Contexts.rend(); I != E; ++I) |
|
{ |
|
DeclContext* Ctx = *I; |
|
|
|
switch(Ctx->getDeclKind()) |
|
{ |
|
case Decl::Namespace: |
|
{ |
|
const NamespaceDecl* ND = cast<NamespaceDecl>(Ctx); |
|
if (ND->isAnonymousNamespace()) |
|
continue; |
|
auto Name = clix::marshalString<clix::E_UTF8>(ND->getName()); |
|
DC = DC->FindCreateNamespace(Name); |
|
continue; |
|
} |
|
case Decl::LinkageSpec: |
|
{ |
|
const LinkageSpecDecl* LD = cast<LinkageSpecDecl>(Ctx); |
|
continue; |
|
} |
|
case Decl::CXXRecord: |
|
{ |
|
auto RD = cast<CXXRecordDecl>(Ctx); |
|
DC = WalkRecordCXX(RD); |
|
continue; |
|
} |
|
case Decl::ClassTemplateSpecialization: |
|
{ |
|
// FIXME: Ignore ClassTemplateSpecialization namespaces... |
|
// We might be able to translate these to C# nested types. |
|
continue; |
|
} |
|
default: |
|
{ |
|
StringRef Kind = Ctx->getDeclKindName(); |
|
printf("Unhandled declaration context kind: %s\n", Kind); |
|
assert(0 && "Unhandled declaration context kind"); |
|
} } |
|
} |
|
|
|
return DC; |
|
} |
|
|
|
CppSharp::AST::DeclarationContext^ Parser::GetNamespace(clang::Decl *D) |
|
{ |
|
return GetNamespace(D, D->getDeclContext()); |
|
} |
|
|
|
static CppSharp::AST::PrimitiveType WalkBuiltinType(const clang::BuiltinType* Builtin) |
|
{ |
|
using namespace CppSharp::AST; |
|
|
|
assert(Builtin && "Expected a builtin type"); |
|
|
|
switch(Builtin->getKind()) |
|
{ |
|
case clang::BuiltinType::Void: return PrimitiveType::Void; |
|
case clang::BuiltinType::Bool: return PrimitiveType::Bool; |
|
|
|
case clang::BuiltinType::SChar: |
|
case clang::BuiltinType::Char_S: return PrimitiveType::Int8; |
|
|
|
case clang::BuiltinType::UChar: |
|
case clang::BuiltinType::Char_U: return PrimitiveType::UInt8; |
|
|
|
case clang::BuiltinType::WChar_S: |
|
case clang::BuiltinType::WChar_U: return PrimitiveType::WideChar; |
|
|
|
case clang::BuiltinType::Short: return PrimitiveType::Int16; |
|
case clang::BuiltinType::UShort: return PrimitiveType::UInt16; |
|
|
|
case clang::BuiltinType::Int: return PrimitiveType::Int32; |
|
case clang::BuiltinType::UInt: return PrimitiveType::UInt32; |
|
|
|
case clang::BuiltinType::Long: return PrimitiveType::Int32; |
|
case clang::BuiltinType::ULong: return PrimitiveType::UInt32; |
|
|
|
case clang::BuiltinType::LongLong: return PrimitiveType::Int64; |
|
case clang::BuiltinType::ULongLong: return PrimitiveType::UInt64; |
|
|
|
case clang::BuiltinType::Float: return PrimitiveType::Float; |
|
case clang::BuiltinType::Double: return PrimitiveType::Double; |
|
|
|
case clang::BuiltinType::NullPtr: return PrimitiveType::Null; |
|
|
|
default: break; |
|
} |
|
|
|
return PrimitiveType::Null; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
clang::TypeLoc ResolveTypeLoc(clang::TypeLoc TL, clang::TypeLoc::TypeLocClass Class) |
|
{ |
|
using namespace clang; |
|
|
|
auto TypeLocClass = TL.getTypeLocClass(); |
|
|
|
if (TypeLocClass == Class) |
|
{ |
|
return TL; |
|
} |
|
if (TypeLocClass == TypeLoc::Qualified) |
|
{ |
|
auto UTL = TL.getUnqualifiedLoc(); |
|
TL = UTL; |
|
} |
|
else if (TypeLocClass == TypeLoc::Elaborated) |
|
{ |
|
auto ETL = TL.getAs<ElaboratedTypeLoc>(); |
|
auto ITL = ETL.getNextTypeLoc(); |
|
TL = ITL; |
|
} |
|
else if (TypeLocClass == TypeLoc::Paren) |
|
{ |
|
auto PTL = TL.getAs<ParenTypeLoc>(); |
|
TL = PTL.getNextTypeLoc(); |
|
} |
|
|
|
assert(TL.getTypeLocClass() == Class); |
|
return TL; |
|
} |
|
|
|
CppSharp::AST::Type^ Parser::WalkType(clang::QualType QualType, clang::TypeLoc* TL, |
|
bool DesugarType) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
if (QualType.isNull()) |
|
return nullptr; |
|
|
|
const clang::Type* Type = QualType.getTypePtr(); |
|
|
|
if (DesugarType) |
|
{ |
|
clang::QualType Desugared = QualType.getDesugaredType(*AST); |
|
assert(!Desugared.isNull() && "Expected a valid desugared type"); |
|
Type = Desugared.getTypePtr(); |
|
} |
|
|
|
assert(Type && "Expected a valid type"); |
|
switch(Type->getTypeClass()) |
|
{ |
|
case Type::Builtin: |
|
{ |
|
auto Builtin = Type->getAs<clang::BuiltinType>(); |
|
assert(Builtin && "Expected a builtin type"); |
|
|
|
auto BT = gcnew CppSharp::AST::BuiltinType(); |
|
BT->Type = WalkBuiltinType(Builtin); |
|
|
|
return BT; |
|
} |
|
case Type::Enum: |
|
{ |
|
auto ET = Type->getAs<clang::EnumType>(); |
|
EnumDecl* ED = ET->getDecl(); |
|
|
|
auto TT = gcnew CppSharp::AST::TagType(); |
|
TT->Declaration = TT->Declaration = WalkDeclaration(ED, /*IgnoreSystemDecls=*/false); |
|
|
|
return TT; |
|
} |
|
case Type::Pointer: |
|
{ |
|
auto Pointer = Type->getAs<clang::PointerType>(); |
|
|
|
auto P = gcnew CppSharp::AST::PointerType(); |
|
P->Modifier = CppSharp::AST::PointerType::TypeModifier::Pointer; |
|
|
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
|
|
auto Pointee = Pointer->getPointeeType(); |
|
P->QualifiedPointee = GetQualifiedType(Pointee, WalkType(Pointee, &Next)); |
|
|
|
return P; |
|
} |
|
case Type::Typedef: |
|
{ |
|
auto TT = Type->getAs<clang::TypedefType>(); |
|
TypedefNameDecl* TD = TT->getDecl(); |
|
|
|
auto TTL = TD->getTypeSourceInfo()->getTypeLoc(); |
|
auto TDD = safe_cast<CppSharp::AST::TypedefDecl^>(WalkDeclaration(TD, |
|
/*IgnoreSystemDecls=*/false)); |
|
|
|
auto Type = gcnew CppSharp::AST::TypedefType(); |
|
Type->Declaration = TDD; |
|
|
|
return Type; |
|
} |
|
case Type::Decayed: |
|
{ |
|
auto DT = Type->getAs<clang::DecayedType>(); |
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
|
|
auto Type = gcnew CppSharp::AST::DecayedType(); |
|
Type->Decayed = GetQualifiedType(DT->getDecayedType(), WalkType(DT->getDecayedType(), &Next)); |
|
Type->Original = GetQualifiedType(DT->getOriginalType(), WalkType(DT->getOriginalType(), &Next)); |
|
Type->Pointee = GetQualifiedType(DT->getPointeeType(), WalkType(DT->getPointeeType(), &Next)); |
|
|
|
return Type; |
|
} |
|
case Type::Elaborated: |
|
{ |
|
auto ET = Type->getAs<clang::ElaboratedType>(); |
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
return WalkType(ET->getNamedType(), &Next); |
|
} |
|
case Type::Record: |
|
{ |
|
auto RT = Type->getAs<clang::RecordType>(); |
|
RecordDecl* RD = RT->getDecl(); |
|
|
|
auto TT = gcnew CppSharp::AST::TagType(); |
|
TT->Declaration = WalkDeclaration(RD, /*IgnoreSystemDecls=*/false); |
|
|
|
return TT; |
|
} |
|
case Type::Paren: |
|
{ |
|
auto PT = Type->getAs<clang::ParenType>(); |
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
return WalkType(PT->getInnerType(), &Next); |
|
} |
|
case Type::ConstantArray: |
|
{ |
|
auto AT = AST->getAsConstantArrayType(QualType); |
|
|
|
auto A = gcnew CppSharp::AST::ArrayType(); |
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
A->Type = WalkType(AT->getElementType(), &Next); |
|
A->SizeType = CppSharp::AST::ArrayType::ArraySize::Constant; |
|
A->Size = AST->getConstantArrayElementCount(AT); |
|
|
|
return A; |
|
} |
|
case Type::IncompleteArray: |
|
{ |
|
auto AT = AST->getAsIncompleteArrayType(QualType); |
|
|
|
auto A = gcnew CppSharp::AST::ArrayType(); |
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
A->Type = WalkType(AT->getElementType(), &Next); |
|
A->SizeType = CppSharp::AST::ArrayType::ArraySize::Incomplete; |
|
|
|
return A; |
|
} |
|
case Type::DependentSizedArray: |
|
{ |
|
auto AT = AST->getAsDependentSizedArrayType(QualType); |
|
|
|
auto A = gcnew CppSharp::AST::ArrayType(); |
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
A->Type = WalkType(AT->getElementType(), &Next); |
|
A->SizeType = CppSharp::AST::ArrayType::ArraySize::Dependent; |
|
//A->Size = AT->getSizeExpr(); |
|
|
|
return A; |
|
} |
|
case Type::FunctionProto: |
|
{ |
|
auto FP = Type->getAs<clang::FunctionProtoType>(); |
|
|
|
auto FTL = TL->getAs<FunctionProtoTypeLoc>(); |
|
auto RL = FTL.getResultLoc(); |
|
|
|
auto F = gcnew CppSharp::AST::FunctionType(); |
|
F->ReturnType = GetQualifiedType(FP->getResultType(), |
|
WalkType(FP->getResultType(), &RL)); |
|
|
|
for (unsigned i = 0; i < FP->getNumArgs(); ++i) |
|
{ |
|
auto FA = gcnew CppSharp::AST::Parameter(); |
|
|
|
auto PVD = FTL.getArg(i); |
|
auto PTL = PVD->getTypeSourceInfo()->getTypeLoc(); |
|
|
|
FA->Name = marshalString<E_UTF8>(PVD->getNameAsString()); |
|
FA->QualifiedType = GetQualifiedType(PVD->getType(), WalkType(PVD->getType(), &PTL)); |
|
|
|
F->Parameters->Add(FA); |
|
} |
|
|
|
return F; |
|
} |
|
case Type::TypeOf: |
|
{ |
|
auto TO = Type->getAs<clang::TypeOfType>(); |
|
return WalkType(TO->getUnderlyingType()); |
|
} |
|
case Type::TypeOfExpr: |
|
{ |
|
auto TO = Type->getAs<clang::TypeOfExprType>(); |
|
return WalkType(TO->getUnderlyingExpr()->getType()); |
|
} |
|
case Type::MemberPointer: |
|
{ |
|
auto MP = Type->getAs<clang::MemberPointerType>(); |
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
|
|
auto MPT = gcnew CppSharp::AST::MemberPointerType(); |
|
MPT->Pointee = WalkType(MP->getPointeeType(), &Next); |
|
|
|
return MPT; |
|
} |
|
case Type::TemplateSpecialization: |
|
{ |
|
auto TS = Type->getAs<clang::TemplateSpecializationType>(); |
|
auto TST = gcnew CppSharp::AST::TemplateSpecializationType(); |
|
|
|
TemplateName Name = TS->getTemplateName(); |
|
TST->Template = safe_cast<CppSharp::AST::Template^>(WalkDeclaration( |
|
Name.getAsTemplateDecl(), 0, /*IgnoreSystemDecls=*/false)); |
|
if (TS->isSugared()) |
|
TST->Desugared = WalkType(TS->desugar()); |
|
|
|
auto TypeLocClass = TL->getTypeLocClass(); |
|
|
|
if (TypeLocClass == TypeLoc::Qualified) |
|
{ |
|
auto UTL = TL->getUnqualifiedLoc(); |
|
TL = &UTL; |
|
} |
|
else if (TypeLocClass == TypeLoc::Elaborated) |
|
{ |
|
auto ETL = TL->getAs<ElaboratedTypeLoc>(); |
|
auto ITL = ETL.getNextTypeLoc(); |
|
TL = &ITL; |
|
} |
|
|
|
assert(TL->getTypeLocClass() == TypeLoc::TemplateSpecialization); |
|
auto TSTL = TL->getAs<TemplateSpecializationTypeLoc>(); |
|
|
|
for (unsigned I = 0, E = TS->getNumArgs(); I != E; ++I) |
|
{ |
|
const TemplateArgument& TA = TS->getArg(I); |
|
auto Arg = CppSharp::AST::TemplateArgument(); |
|
|
|
TemplateArgumentLoc ArgLoc; |
|
ArgLoc = TSTL.getArgLoc(I); |
|
|
|
switch(TA.getKind()) |
|
{ |
|
case TemplateArgument::Type: |
|
{ |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Type; |
|
TypeLoc ArgTL; |
|
ArgTL = ArgLoc.getTypeSourceInfo()->getTypeLoc(); |
|
Arg.Type = GetQualifiedType(TA.getAsType(), WalkType(TA.getAsType(), &ArgTL)); |
|
break; |
|
} |
|
case TemplateArgument::Declaration: |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Declaration; |
|
Arg.Declaration = WalkDeclaration(TA.getAsDecl(), 0); |
|
break; |
|
case TemplateArgument::NullPtr: |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::NullPtr; |
|
break; |
|
case TemplateArgument::Integral: |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Integral; |
|
//Arg.Type = WalkType(TA.getIntegralType(), 0); |
|
Arg.Integral = TA.getAsIntegral().getLimitedValue(); |
|
break; |
|
case TemplateArgument::Template: |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Template; |
|
break; |
|
case TemplateArgument::TemplateExpansion: |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::TemplateExpansion; |
|
break; |
|
case TemplateArgument::Expression: |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Expression; |
|
break; |
|
case TemplateArgument::Pack: |
|
Arg.Kind = CppSharp::AST::TemplateArgument::ArgumentKind::Pack; |
|
break; |
|
} |
|
|
|
TST->Arguments->Add(Arg); |
|
} |
|
|
|
return TST; |
|
} |
|
case Type::TemplateTypeParm: |
|
{ |
|
auto TP = Type->getAs<TemplateTypeParmType>(); |
|
|
|
auto TPT = gcnew CppSharp::AST::TemplateParameterType(); |
|
|
|
if (auto Ident = TP->getIdentifier()) |
|
TPT->Parameter.Name = marshalString<E_UTF8>(Ident->getName()); |
|
|
|
return TPT; |
|
} |
|
case Type::SubstTemplateTypeParm: |
|
{ |
|
auto TP = Type->getAs<SubstTemplateTypeParmType>(); |
|
auto Ty = TP->getReplacementType(); |
|
auto TPT = gcnew CppSharp::AST::TemplateParameterSubstitutionType(); |
|
|
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
TPT->Replacement = GetQualifiedType(Ty, WalkType(Ty, &Next)); |
|
|
|
return TPT; |
|
} |
|
case Type::InjectedClassName: |
|
{ |
|
auto ICN = Type->getAs<InjectedClassNameType>(); |
|
auto ICNT = gcnew CppSharp::AST::InjectedClassNameType(); |
|
ICNT->Class = safe_cast<CppSharp::AST::Class^>(WalkDeclaration( |
|
ICN->getDecl(), 0, /*IgnoreSystemDecls=*/false)); |
|
return ICNT; |
|
} |
|
case Type::DependentName: |
|
{ |
|
auto DN = Type->getAs<DependentNameType>(); |
|
auto DNT = gcnew CppSharp::AST::DependentNameType(); |
|
return DNT; |
|
} |
|
case Type::LValueReference: |
|
{ |
|
auto LR = Type->getAs<clang::LValueReferenceType>(); |
|
|
|
auto P = gcnew CppSharp::AST::PointerType(); |
|
P->Modifier = CppSharp::AST::PointerType::TypeModifier::LVReference; |
|
|
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
|
|
auto Pointee = LR->getPointeeType(); |
|
P->QualifiedPointee = GetQualifiedType(Pointee, WalkType(Pointee, &Next)); |
|
|
|
return P; |
|
} |
|
case Type::RValueReference: |
|
{ |
|
auto LR = Type->getAs<clang::RValueReferenceType>(); |
|
|
|
auto P = gcnew CppSharp::AST::PointerType(); |
|
P->Modifier = CppSharp::AST::PointerType::TypeModifier::RVReference; |
|
|
|
TypeLoc Next; |
|
if (!TL->isNull()) Next = TL->getNextTypeLoc(); |
|
|
|
auto Pointee = LR->getPointeeType(); |
|
P->QualifiedPointee = GetQualifiedType(Pointee, WalkType(Pointee, &Next)); |
|
|
|
return P; |
|
} |
|
case Type::Vector: |
|
{ |
|
// GCC-specific / __attribute__((vector_size(n)) |
|
return nullptr; |
|
} |
|
case Type::PackExpansion: |
|
{ |
|
// Ignored. |
|
return nullptr; |
|
} |
|
default: |
|
{ |
|
Debug("Unhandled type class '%s'\n", Type->getTypeClassName()); |
|
return nullptr; |
|
} } |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::Enumeration^ Parser::WalkEnum(clang::EnumDecl* ED) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
auto NS = GetNamespace(ED); |
|
assert(NS && "Expected a valid namespace"); |
|
|
|
auto Name = marshalString<E_UTF8>(GetTagDeclName(ED)); |
|
auto E = NS->FindEnum(Name, /*Create=*/false); |
|
|
|
if (E && !E->IsIncomplete) |
|
return E; |
|
|
|
if (!E) |
|
E = NS->FindEnum(Name, /*Create=*/true); |
|
|
|
if (ED->isScoped()) |
|
E->Modifiers |= CppSharp::AST::Enumeration::EnumModifiers::Scoped; |
|
|
|
// Get the underlying integer backing the enum. |
|
QualType IntType = ED->getIntegerType(); |
|
E->Type = WalkType(IntType, 0); |
|
E->BuiltinType = safe_cast<CppSharp::AST::BuiltinType^>(WalkType(IntType, 0, |
|
/*DesugarType=*/true)); |
|
|
|
if (!ED->isThisDeclarationADefinition()) |
|
{ |
|
E->IsIncomplete = true; |
|
return E; |
|
} |
|
|
|
E->IsIncomplete = false; |
|
for(auto it = ED->enumerator_begin(); it != ED->enumerator_end(); ++it) |
|
{ |
|
EnumConstantDecl* ECD = (*it); |
|
|
|
std::string BriefText; |
|
if (const RawComment* Comment = AST->getRawCommentForAnyRedecl(ECD)) |
|
BriefText = Comment->getBriefText(*AST); |
|
|
|
auto EnumItem = gcnew CppSharp::AST::Enumeration::Item(); |
|
EnumItem->Name = marshalString<E_UTF8>(ECD->getNameAsString()); |
|
auto Value = ECD->getInitVal(); |
|
EnumItem->Value = Value.isSigned() ? Value.getSExtValue() |
|
: Value.getZExtValue(); |
|
EnumItem->Comment = marshalString<E_UTF8>(BriefText); |
|
|
|
std::string Text; |
|
if (GetDeclText(ECD->getSourceRange(), Text)) |
|
EnumItem->Expression = marshalString<E_UTF8>(Text); |
|
|
|
E->AddItem(EnumItem); |
|
} |
|
|
|
return E; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
clang::CallingConv Parser::GetAbiCallConv(clang::CallingConv CC, |
|
bool IsInstMethod, |
|
bool IsVariadic) |
|
{ |
|
using namespace clang; |
|
|
|
// TODO: Itanium ABI |
|
|
|
if (CC == CC_Default) { |
|
if (IsInstMethod) { |
|
CC = AST->getDefaultCXXMethodCallConv(IsVariadic); |
|
} else { |
|
CC = CC_C; |
|
} |
|
} |
|
|
|
return CC; |
|
} |
|
|
|
static CppSharp::AST::CallingConvention ConvertCallConv(clang::CallingConv CC) |
|
{ |
|
using namespace clang; |
|
|
|
switch(CC) |
|
{ |
|
case CC_Default: |
|
case CC_C: |
|
return CppSharp::AST::CallingConvention::C; |
|
case CC_X86StdCall: |
|
return CppSharp::AST::CallingConvention::StdCall; |
|
case CC_X86FastCall: |
|
return CppSharp::AST::CallingConvention::FastCall; |
|
case CC_X86ThisCall: |
|
return CppSharp::AST::CallingConvention::ThisCall; |
|
case CC_X86Pascal: |
|
case CC_AAPCS: |
|
case CC_AAPCS_VFP: |
|
return CppSharp::AST::CallingConvention::Unknown; |
|
} |
|
|
|
return CppSharp::AST::CallingConvention::Default; |
|
} |
|
|
|
void Parser::WalkFunction(clang::FunctionDecl* FD, CppSharp::AST::Function^ F, |
|
bool IsDependent) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
assert (FD->getBuiltinID() == 0); |
|
|
|
auto FT = FD->getType()->getAs<FunctionType>(); |
|
auto CC = FT->getCallConv(); |
|
|
|
auto NS = GetNamespace(FD); |
|
assert(NS && "Expected a valid namespace"); |
|
|
|
F->OriginalPtr = System::IntPtr(FD); |
|
F->Name = marshalString<E_UTF8>(FD->getNameAsString()); |
|
F->Namespace = NS; |
|
F->IsVariadic = FD->isVariadic(); |
|
F->IsInline = FD->isInlined(); |
|
F->IsDependent = FD->isDependentContext(); |
|
F->IsPure = FD->isPure(); |
|
|
|
auto AbiCC = GetAbiCallConv(CC, FD->isCXXInstanceMember(), FD->isVariadic()); |
|
F->CallingConvention = ConvertCallConv(AbiCC); |
|
|
|
TypeLoc RTL; |
|
if (auto TSI = FD->getTypeSourceInfo()) |
|
{ |
|
FunctionTypeLoc FTL = TSI->getTypeLoc().getAs<FunctionTypeLoc>(); |
|
if (FTL) |
|
{ |
|
RTL = FTL.getResultLoc(); |
|
|
|
auto &SM = C->getSourceManager(); |
|
auto headStartLoc = GetDeclStartLocation(C.get(), FD); |
|
auto headEndLoc = SM.getExpansionLoc(FTL.getLParenLoc()); |
|
auto headRange = clang::SourceRange(headStartLoc, headEndLoc); |
|
|
|
HandlePreprocessedEntities(F, headRange, CppSharp::AST::MacroLocation::FunctionHead); |
|
HandlePreprocessedEntities(F, FTL.getParensRange(), CppSharp::AST::MacroLocation::FunctionParameters); |
|
//auto bodyRange = clang::SourceRange(FTL.getRParenLoc(), FD->getLocEnd()); |
|
//HandlePreprocessedEntities(F, bodyRange, CppSharp::AST::MacroLocation::FunctionBody); |
|
} |
|
} |
|
|
|
F->ReturnType = GetQualifiedType(FD->getResultType(), |
|
WalkType(FD->getResultType(), &RTL)); |
|
|
|
String Mangled = GetDeclMangledName(FD, TargetABI, IsDependent); |
|
F->Mangled = marshalString<E_UTF8>(Mangled); |
|
|
|
for(auto it = FD->param_begin(); it != FD->param_end(); ++it) |
|
{ |
|
ParmVarDecl* VD = (*it); |
|
|
|
auto P = gcnew CppSharp::AST::Parameter(); |
|
P->Name = marshalString<E_UTF8>(VD->getNameAsString()); |
|
|
|
TypeLoc PTL; |
|
if (auto TSI = VD->getTypeSourceInfo()) |
|
PTL = VD->getTypeSourceInfo()->getTypeLoc(); |
|
|
|
P->QualifiedType = GetQualifiedType(VD->getType(), WalkType(VD->getType(), &PTL)); |
|
P->HasDefaultValue = VD->hasDefaultArg(); |
|
P->Namespace = NS; |
|
|
|
F->Parameters->Add(P); |
|
} |
|
} |
|
|
|
CppSharp::AST::Function^ Parser::WalkFunction(clang::FunctionDecl* FD, bool IsDependent, |
|
bool AddToNamespace) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
assert (FD->getBuiltinID() == 0); |
|
|
|
auto NS = GetNamespace(FD); |
|
assert(NS && "Expected a valid namespace"); |
|
|
|
auto Name = marshalString<E_UTF8>(FD->getNameAsString()); |
|
CppSharp::AST::Function^ F = NS->FindFunction(Name, /*Create=*/ false); |
|
|
|
if (F != nullptr) |
|
return F; |
|
|
|
F = gcnew CppSharp::AST::Function(); |
|
WalkFunction(FD, F, IsDependent); |
|
|
|
if (AddToNamespace) |
|
NS->Functions->Add(F); |
|
|
|
return F; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
SourceLocationKind Parser::GetLocationKind(const clang::SourceLocation& Loc) |
|
{ |
|
using namespace clang; |
|
|
|
SourceManager& SM = C->getSourceManager(); |
|
PresumedLoc PLoc = SM.getPresumedLoc(Loc); |
|
|
|
if(PLoc.isInvalid()) |
|
return SourceLocationKind::Invalid; |
|
|
|
const char *FileName = PLoc.getFilename(); |
|
|
|
if(strcmp(FileName, "<built-in>") == 0) |
|
return SourceLocationKind::Builtin; |
|
|
|
if(strcmp(FileName, "<command line>") == 0) |
|
return SourceLocationKind::CommandLine; |
|
|
|
if(SM.getFileCharacteristic(Loc) == clang::SrcMgr::C_User) |
|
return SourceLocationKind::User; |
|
|
|
return SourceLocationKind::System; |
|
} |
|
|
|
bool Parser::IsValidDeclaration(const clang::SourceLocation& Loc) |
|
{ |
|
auto Kind = GetLocationKind(Loc); |
|
|
|
return Kind == SourceLocationKind::User; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
void Parser::WalkAST() |
|
{ |
|
using namespace clang; |
|
|
|
if (C->hasPreprocessor()) |
|
{ |
|
Preprocessor& P = C->getPreprocessor(); |
|
PreprocessingRecord* PR = P.getPreprocessingRecord(); |
|
|
|
if (PR) |
|
{ |
|
assert(PR && "Expected a valid preprocessing record"); |
|
WalkMacros(PR); |
|
} |
|
} |
|
|
|
TranslationUnitDecl* TU = AST->getTranslationUnitDecl(); |
|
|
|
for(auto it = TU->decls_begin(); it != TU->decls_end(); ++it) |
|
{ |
|
Decl* D = (*it); |
|
WalkDeclarationDef(D); |
|
} |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
void Parser::WalkMacros(clang::PreprocessingRecord* PR) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
Preprocessor& P = C->getPreprocessor(); |
|
|
|
for(auto it = PR->begin(); it != PR->end(); ++it) |
|
{ |
|
const PreprocessedEntity* PE = (*it); |
|
|
|
switch(PE->getKind()) |
|
{ |
|
case PreprocessedEntity::MacroDefinitionKind: |
|
{ |
|
const MacroDefinition* MD = cast<MacroDefinition>(PE); |
|
|
|
if (!IsValidDeclaration(MD->getLocation())) |
|
break; |
|
|
|
const IdentifierInfo* II = MD->getName(); |
|
assert(II && "Expected valid identifier info"); |
|
|
|
MacroInfo* MI = P.getMacroInfo((IdentifierInfo*)II); |
|
|
|
if (!MI || MI->isBuiltinMacro() || MI->isFunctionLike()) |
|
continue; |
|
|
|
SourceManager& SM = C->getSourceManager(); |
|
const LangOptions &LangOpts = C->getLangOpts(); |
|
|
|
auto Loc = MI->getDefinitionLoc(); |
|
|
|
if (!IsValidDeclaration(Loc)) |
|
break; |
|
|
|
SourceLocation BeginExpr = |
|
Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); |
|
|
|
auto Range = CharSourceRange::getTokenRange( |
|
BeginExpr, MI->getDefinitionEndLoc()); |
|
|
|
bool Invalid; |
|
StringRef Expression = Lexer::getSourceText(Range, SM, LangOpts, |
|
&Invalid); |
|
|
|
if (Invalid || Expression.empty()) |
|
break; |
|
|
|
auto macro = gcnew CppSharp::AST::MacroDefinition(); |
|
macro->Name = marshalString<E_UTF8>(II->getName())->Trim(); |
|
macro->Expression = marshalString<E_UTF8>(Expression)->Trim(); |
|
|
|
auto M = GetTranslationUnit(BeginExpr); |
|
if( M != nullptr ) |
|
M->Macros->Add(macro); |
|
|
|
break; |
|
} |
|
default: break; |
|
} |
|
} |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::Variable^ Parser::WalkVariable(clang::VarDecl *VD) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
auto Var = gcnew CppSharp::AST::Variable(); |
|
Var->Name = marshalString<E_UTF8>(VD->getName()); |
|
Var->Access = ConvertToAccess(VD->getAccess()); |
|
|
|
auto TL = VD->getTypeSourceInfo()->getTypeLoc(); |
|
Var->QualifiedType = GetQualifiedType(VD->getType(), WalkType(VD->getType(), &TL)); |
|
|
|
auto Mangled = GetDeclMangledName(VD, TargetABI, /*IsDependent=*/false); |
|
Var->Mangled = marshalString<E_UTF8>(Mangled); |
|
|
|
return Var; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
bool Parser::GetDeclText(clang::SourceRange SR, std::string& Text) |
|
{ |
|
using namespace clang; |
|
SourceManager& SM = C->getSourceManager(); |
|
const LangOptions &LangOpts = C->getLangOpts(); |
|
|
|
auto Range = CharSourceRange::getTokenRange(SR); |
|
|
|
bool Invalid; |
|
Text = Lexer::getSourceText(Range, SM, LangOpts, &Invalid); |
|
|
|
return !Invalid && !Text.empty(); |
|
} |
|
|
|
void Parser::HandlePreprocessedEntities(CppSharp::AST::Declaration^ Decl, |
|
clang::SourceRange sourceRange, |
|
CppSharp::AST::MacroLocation macroLocation) |
|
{ |
|
using namespace clang; |
|
auto PPRecord = C->getPreprocessor().getPreprocessingRecord(); |
|
|
|
auto Range = PPRecord->getPreprocessedEntitiesInRange(sourceRange); |
|
|
|
for (auto it = Range.first; it != Range.second; ++it) |
|
{ |
|
PreprocessedEntity* PPEntity = (*it); |
|
|
|
CppSharp::AST::PreprocessedEntity^ Entity; |
|
switch(PPEntity->getKind()) |
|
{ |
|
case PreprocessedEntity::MacroExpansionKind: |
|
{ |
|
const MacroExpansion* MD = cast<MacroExpansion>(PPEntity); |
|
Entity = gcnew CppSharp::AST::MacroExpansion(); |
|
|
|
std::string Text; |
|
if (!GetDeclText(PPEntity->getSourceRange(), Text)) |
|
continue; |
|
|
|
static_cast<CppSharp::AST::MacroExpansion^>(Entity)->Text = |
|
clix::marshalString<clix::E_UTF8>(Text); |
|
break; |
|
} |
|
case PreprocessedEntity::MacroDefinitionKind: |
|
{ |
|
const MacroDefinition* MD = cast<MacroDefinition>(PPEntity); |
|
Entity = gcnew CppSharp::AST::MacroDefinition(); |
|
break; |
|
} |
|
default: |
|
continue; |
|
} |
|
|
|
Entity->Location = macroLocation; |
|
|
|
Decl->PreprocessedEntities->Add(Entity); |
|
} |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
CppSharp::AST::Declaration^ Parser::WalkDeclarationDef(clang::Decl* D) |
|
{ |
|
return WalkDeclaration(D, /*IgnoreSystemDecls=*/true, |
|
/*CanBeDefinition=*/true); |
|
} |
|
|
|
CppSharp::AST::Declaration^ Parser::WalkDeclaration(clang::Decl* D, |
|
bool IgnoreSystemDecls, |
|
bool CanBeDefinition) |
|
{ |
|
using namespace clang; |
|
using namespace clix; |
|
|
|
// Ignore declarations that do not come from user-provided |
|
// header files. |
|
if (IgnoreSystemDecls && !IsValidDeclaration(D->getLocation())) |
|
return nullptr; |
|
|
|
for(auto it = D->attr_begin(); it != D->attr_end(); ++it) |
|
{ |
|
Attr* Attr = (*it); |
|
|
|
if(Attr->getKind() != clang::attr::Annotate) |
|
continue; |
|
|
|
AnnotateAttr* Annotation = cast<AnnotateAttr>(Attr); |
|
assert(Annotation != nullptr); |
|
|
|
StringRef AnnotationText = Annotation->getAnnotation(); |
|
} |
|
|
|
CppSharp::AST::Declaration^ Decl = nullptr; |
|
|
|
auto Kind = D->getKind(); |
|
switch(D->getKind()) |
|
{ |
|
case Decl::CXXRecord: |
|
{ |
|
CXXRecordDecl* RD = cast<CXXRecordDecl>(D); |
|
|
|
auto Class = WalkRecordCXX(RD); |
|
|
|
// We store a definition order index into the declarations. |
|
// This is needed because declarations are added to their contexts as |
|
// soon as they are referenced and we need to know the original order |
|
// of the declarations. |
|
|
|
if (CanBeDefinition && Class->DefinitionOrder == 0) |
|
{ |
|
Class->DefinitionOrder = Index++; |
|
//Debug("%d: %s\n", Index++, GetTagDeclName(RD).c_str()); |
|
} |
|
|
|
Decl = Class; |
|
break; |
|
} |
|
case Decl::ClassTemplate: |
|
{ |
|
ClassTemplateDecl* TD = cast<ClassTemplateDecl>(D); |
|
auto Template = WalkClassTemplate(TD); |
|
|
|
auto NS = GetNamespace(TD); |
|
Template->Namespace = NS; |
|
NS->Templates->Add(Template); |
|
|
|
Decl = Template; |
|
break; |
|
} |
|
case Decl::ClassTemplateSpecialization: |
|
{ |
|
auto TS = cast<ClassTemplateSpecializationDecl>(D); |
|
auto CT = gcnew CppSharp::AST::ClassTemplateSpecialization(); |
|
|
|
Decl = CT; |
|
break; |
|
} |
|
case Decl::ClassTemplatePartialSpecialization: |
|
{ |
|
auto TS = cast<ClassTemplatePartialSpecializationDecl>(D); |
|
auto CT = gcnew CppSharp::AST::ClassTemplatePartialSpecialization(); |
|
|
|
Decl = CT; |
|
break; |
|
} |
|
case Decl::FunctionTemplate: |
|
{ |
|
FunctionTemplateDecl* TD = cast<FunctionTemplateDecl>(D); |
|
auto Template = WalkFunctionTemplate(TD); |
|
|
|
auto NS = GetNamespace(TD); |
|
Template->Namespace = NS; |
|
NS->Templates->Add(Template); |
|
|
|
Decl = Template; |
|
break; |
|
} |
|
case Decl::Enum: |
|
{ |
|
EnumDecl* ED = cast<EnumDecl>(D); |
|
Decl = WalkEnum(ED); |
|
break; |
|
} |
|
case Decl::Function: |
|
{ |
|
FunctionDecl* FD = cast<FunctionDecl>(D); |
|
if (!FD->isFirstDeclaration()) |
|
break; |
|
|
|
// Check for and ignore built-in functions. |
|
if (FD->getBuiltinID() != 0) |
|
break; |
|
|
|
Decl = WalkFunction(FD); |
|
break; |
|
} |
|
case Decl::LinkageSpec: |
|
{ |
|
LinkageSpecDecl* LS = cast<LinkageSpecDecl>(D); |
|
|
|
for (auto it = LS->decls_begin(); it != LS->decls_end(); ++it) |
|
{ |
|
clang::Decl* D = (*it); |
|
Decl = WalkDeclarationDef(D); |
|
} |
|
|
|
break; |
|
} |
|
case Decl::Typedef: |
|
{ |
|
TypedefDecl* TD = cast<TypedefDecl>(D); |
|
|
|
auto NS = GetNamespace(TD); |
|
auto Name = marshalString<E_UTF8>(GetDeclName(TD)); |
|
auto Typedef = NS->FindTypedef(Name, /*Create=*/false); |
|
if (Typedef) return Typedef; |
|
|
|
Typedef = NS->FindTypedef(Name, /*Create=*/true); |
|
|
|
auto TTL = TD->getTypeSourceInfo()->getTypeLoc(); |
|
Typedef->QualifiedType = GetQualifiedType(TD->getUnderlyingType(), |
|
WalkType(TD->getUnderlyingType(), &TTL)); |
|
|
|
Decl = Typedef; |
|
break; |
|
} |
|
case Decl::Namespace: |
|
{ |
|
NamespaceDecl* ND = cast<NamespaceDecl>(D); |
|
|
|
for (auto it = ND->decls_begin(); it != ND->decls_end(); ++it) |
|
{ |
|
clang::Decl* D = (*it); |
|
Decl = WalkDeclarationDef(D); |
|
} |
|
|
|
break; |
|
} |
|
case Decl::Var: |
|
{ |
|
auto VD = cast<VarDecl>(D); |
|
Decl = WalkVariable(VD); |
|
|
|
auto NS = GetNamespace(VD); |
|
Decl->Namespace = NS; |
|
NS->Variables->Add(static_cast<CppSharp::AST::Variable^>(Decl)); |
|
break; |
|
} |
|
// Ignore these declarations since they must have been declared in |
|
// a class already. |
|
case Decl::CXXConstructor: |
|
case Decl::CXXDestructor: |
|
case Decl::CXXConversion: |
|
case Decl::CXXMethod: |
|
break; |
|
case Decl::Empty: |
|
case Decl::AccessSpec: |
|
case Decl::Friend: |
|
case Decl::Using: |
|
case Decl::UsingShadow: |
|
case Decl::UnresolvedUsingTypename: |
|
case Decl::UnresolvedUsingValue: |
|
case Decl::IndirectField: |
|
break; |
|
default: |
|
{ |
|
Debug("Unhandled declaration kind: %s\n", D->getDeclKindName()); |
|
|
|
auto &SM = C->getSourceManager(); |
|
auto Loc = D->getLocation(); |
|
auto FileName = SM.getFilename(Loc); |
|
auto Offset = SM.getFileOffset(Loc); |
|
auto LineNo = SM.getLineNumber(SM.getFileID(Loc), Offset); |
|
Debug(" %s (line %u)\n", FileName, LineNo); |
|
|
|
break; |
|
} }; |
|
|
|
if (Decl) |
|
{ |
|
if (Decl->PreprocessedEntities->Count == 0) |
|
{ |
|
auto startLoc = GetDeclStartLocation(C.get(), D); |
|
auto endLoc = D->getLocEnd(); |
|
auto range = clang::SourceRange(startLoc, endLoc); |
|
|
|
HandlePreprocessedEntities(Decl, range); |
|
} |
|
HandleComments(D, Decl); |
|
|
|
if (const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D)) |
|
Decl->IsDependent = VD->getType()->isDependentType(); |
|
} |
|
|
|
return Decl; |
|
} |
|
|
|
//-----------------------------------// |
|
|
|
struct Diagnostic |
|
{ |
|
clang::SourceLocation Location; |
|
llvm::SmallString<100> Message; |
|
clang::DiagnosticsEngine::Level Level; |
|
}; |
|
|
|
struct DiagnosticConsumer : public clang::DiagnosticConsumer |
|
{ |
|
virtual ~DiagnosticConsumer() { } |
|
|
|
virtual void HandleDiagnostic(clang::DiagnosticsEngine::Level Level, |
|
const clang::Diagnostic& Info) override { |
|
auto Diag = Diagnostic(); |
|
Diag.Location = Info.getLocation(); |
|
Diag.Level = Level; |
|
Info.FormatDiagnostic(Diag.Message); |
|
Diagnostics.push_back(Diag); |
|
} |
|
|
|
std::vector<Diagnostic> Diagnostics; |
|
}; |
|
|
|
ParserResult^ Parser::ParseHeader(const std::string& File) |
|
{ |
|
auto res = gcnew ParserResult(); |
|
res->Library = Lib; |
|
|
|
if (File.empty()) |
|
{ |
|
res->Kind = ParserResultKind::FileNotFound; |
|
return res; |
|
} |
|
|
|
SetupHeader(); |
|
|
|
auto SC = new clang::SemaConsumer(); |
|
C->setASTConsumer(SC); |
|
|
|
C->createSema(clang::TU_Complete, 0); |
|
SC->InitializeSema(C->getSema()); |
|
|
|
auto DiagClient = new DiagnosticConsumer(); |
|
C->getDiagnostics().setClient(DiagClient); |
|
|
|
// Check that the file is reachable. |
|
const clang::DirectoryLookup *Dir; |
|
if (!C->getPreprocessor().getHeaderSearchInfo().LookupFile(File, /*isAngled*/true, |
|
nullptr, Dir, nullptr, nullptr, nullptr, nullptr)) |
|
{ |
|
res->Kind = ParserResultKind::FileNotFound; |
|
return res; |
|
} |
|
|
|
// Create a virtual file that includes the header. This gets rid of some |
|
// Clang warnings about parsing an header file as the main file. |
|
|
|
std::string str; |
|
str += "#include \"" + File + "\"" + "\n"; |
|
str += "\0"; |
|
|
|
auto buffer = llvm::MemoryBuffer::getMemBuffer(str); |
|
C->getSourceManager().createMainFileIDForMemBuffer(buffer); |
|
|
|
clang::DiagnosticConsumer* client = C->getDiagnostics().getClient(); |
|
client->BeginSourceFile(C->getLangOpts(), &C->getPreprocessor()); |
|
|
|
ParseAST(C->getSema(), /*PrintStats=*/false, /*SkipFunctionBodies=*/true); |
|
|
|
client->EndSourceFile(); |
|
|
|
// Convert the diagnostics to the managed types |
|
for each (auto& Diag in DiagClient->Diagnostics) |
|
{ |
|
using namespace clix; |
|
|
|
auto& Source = C->getSourceManager(); |
|
auto FileName = Source.getFilename(Diag.Location); |
|
|
|
auto PDiag = ParserDiagnostic(); |
|
PDiag.FileName = marshalString<E_UTF8>(FileName.str()); |
|
PDiag.Message = marshalString<E_UTF8>(Diag.Message.str()); |
|
PDiag.LineNumber = 0; |
|
PDiag.ColumnNumber = 0; |
|
|
|
if( !Diag.Location.isInvalid() ) |
|
{ |
|
clang::PresumedLoc PLoc = Source.getPresumedLoc(Diag.Location); |
|
if( PLoc.isValid() ) |
|
{ |
|
PDiag.LineNumber = PLoc.getLine(); |
|
PDiag.ColumnNumber = PLoc.getColumn(); |
|
} |
|
} |
|
|
|
switch( Diag.Level ) |
|
{ |
|
case clang::DiagnosticsEngine::Ignored: |
|
PDiag.Level = ParserDiagnosticLevel::Ignored; |
|
break; |
|
case clang::DiagnosticsEngine::Note: |
|
PDiag.Level = ParserDiagnosticLevel::Note; |
|
break; |
|
case clang::DiagnosticsEngine::Warning: |
|
PDiag.Level = ParserDiagnosticLevel::Warning; |
|
break; |
|
case clang::DiagnosticsEngine::Error: |
|
PDiag.Level = ParserDiagnosticLevel::Error; |
|
break; |
|
case clang::DiagnosticsEngine::Fatal: |
|
PDiag.Level = ParserDiagnosticLevel::Fatal; |
|
break; |
|
default: |
|
assert(0); |
|
} |
|
|
|
res->Diagnostics->Add(PDiag); |
|
} |
|
|
|
if(C->getDiagnosticClient().getNumErrors() != 0) |
|
{ |
|
res->Kind = ParserResultKind::Error; |
|
return res; |
|
} |
|
|
|
AST = &C->getASTContext(); |
|
WalkAST(); |
|
|
|
res->Kind = ParserResultKind::Success; |
|
return res; |
|
} |
|
|
|
ParserResultKind Parser::ParseArchive(llvm::StringRef File, |
|
llvm::MemoryBuffer *Buffer) |
|
{ |
|
llvm::error_code Code; |
|
llvm::object::Archive Archive(Buffer, Code); |
|
|
|
if (Code) |
|
return ParserResultKind::Error; |
|
|
|
auto LibName = clix::marshalString<clix::E_UTF8>(File); |
|
auto NativeLib = Lib->FindOrCreateLibrary(LibName); |
|
|
|
for(auto it = Archive.begin_symbols(); it != Archive.end_symbols(); ++it) |
|
{ |
|
llvm::StringRef SymRef; |
|
|
|
if (it->getName(SymRef)) |
|
continue; |
|
|
|
System::String^ SymName = clix::marshalString<clix::E_UTF8>(SymRef); |
|
NativeLib->Symbols->Add(SymName); |
|
} |
|
|
|
return ParserResultKind::Success; |
|
} |
|
|
|
ParserResultKind Parser::ParseSharedLib(llvm::StringRef File, |
|
llvm::MemoryBuffer *Buffer) |
|
{ |
|
auto Object = llvm::object::ObjectFile::createObjectFile(Buffer); |
|
|
|
if (!Object) |
|
return ParserResultKind::Error; |
|
|
|
auto LibName = clix::marshalString<clix::E_UTF8>(File); |
|
auto NativeLib = Lib->FindOrCreateLibrary(LibName); |
|
|
|
llvm::error_code ec; |
|
for(auto it = Object->begin_symbols(); it != Object->end_symbols(); it.increment(ec)) |
|
{ |
|
llvm::StringRef SymRef; |
|
|
|
if (it->getName(SymRef)) |
|
continue; |
|
|
|
System::String^ SymName = clix::marshalString<clix::E_UTF8>(SymRef); |
|
NativeLib->Symbols->Add(SymName); |
|
} |
|
|
|
for(auto it = Object->begin_dynamic_symbols(); it != Object->end_dynamic_symbols(); |
|
it.increment(ec)) |
|
{ |
|
llvm::StringRef SymRef; |
|
|
|
if (it->getName(SymRef)) |
|
continue; |
|
|
|
System::String^ SymName = clix::marshalString<clix::E_UTF8>(SymRef); |
|
NativeLib->Symbols->Add(SymName); |
|
} |
|
|
|
return ParserResultKind::Success; |
|
} |
|
|
|
ParserResult^ Parser::ParseLibrary(const std::string& File) |
|
{ |
|
auto res = gcnew ParserResult(); |
|
res->Library = Lib; |
|
|
|
if (File.empty()) |
|
{ |
|
res->Kind = ParserResultKind::FileNotFound; |
|
return res; |
|
} |
|
|
|
C.reset(new clang::CompilerInstance()); |
|
C->createFileManager(); |
|
|
|
auto &FM = C->getFileManager(); |
|
const clang::FileEntry* FileEntry = 0; |
|
|
|
for each(System::String^ LibDir in Opts->LibraryDirs) |
|
{ |
|
auto DirName = clix::marshalString<clix::E_UTF8>(LibDir); |
|
llvm::SmallString<256> Path(DirName); |
|
llvm::sys::path::append(Path, File); |
|
|
|
if (FileEntry = FM.getFile(Path.str())) |
|
break; |
|
} |
|
|
|
if (!FileEntry) |
|
{ |
|
res->Kind = ParserResultKind::FileNotFound; |
|
return res; |
|
} |
|
|
|
res->Kind = ParseArchive(File, FM.getBufferForFile(FileEntry)); |
|
if (res->Kind == ParserResultKind::Success) |
|
return res; |
|
|
|
res->Kind = ParseSharedLib(File, FM.getBufferForFile(FileEntry)); |
|
if (res->Kind == ParserResultKind::Success) |
|
return res; |
|
|
|
return res; |
|
} |