/************************************************************************ * * Cxxi * Licensed under the simplified BSD license. All rights reserved. * ************************************************************************/ #include "Parser.h" #include "Interop.h" #include #include #include #include #include #include #include #include #include #include #include #include //-----------------------------------// Parser::Parser(ParserOptions^ Opts) : Lib(Opts->Library), Index(0) { Setup(Opts); } //-----------------------------------// 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(); } //-----------------------------------// void Parser::Setup(ParserOptions^ Opts) { using namespace clang; using namespace clix; const char* args[] = { // Enable C++ language mode "-xc++", "-std=c++11", "-fno-rtti", // Enable the Microsoft parsing extensions "-fms-extensions", "-fms-compatibility", "-fdelayed-template-parsing", // Enable the Microsoft ABI //"-Xclang", "-cxx-abi", "-Xclang", "microsoft" }; C.reset(new CompilerInstance()); C->createDiagnostics(ARRAY_SIZE(args), args); CompilerInvocation* Inv = new CompilerInvocation(); CompilerInvocation::CreateFromArgs(*Inv, args, args + ARRAY_SIZE(args), C->getDiagnostics()); C->setInvocation(Inv); TargetOptions& TO = Inv->getTargetOpts(); TO.Triple = llvm::sys::getDefaultTargetTriple(); TargetInfo* TI = TargetInfo::CreateTargetInfo(C->getDiagnostics(), &TO); TI->setCXXABI(CXXABI_Microsoft); C->setTarget(TI); C->createFileManager(); C->createSourceManager(C->getFileManager()); if (Opts->Verbose) C->getHeaderSearchOpts().Verbose = true; for each(System::String^% include in Opts->IncludeDirs) { String s = marshalString(include); C->getHeaderSearchOpts().AddPath(s, frontend::Angled, true, false, true); } // Initialize the default platform headers. std::string ResourceDir = GetClangResourceDir("."); C->getHeaderSearchOpts().ResourceDir = ResourceDir; C->getHeaderSearchOpts().AddPath(GetClangBuiltinIncludeDir(), clang::frontend::System, false, false, true); #ifdef _WIN32 std::vector SystemDirs = clang::driver::GetWindowsSystemIncludeDirs(); clang::HeaderSearchOptions& HSOpts = C->getHeaderSearchOpts(); for(size_t i = 0; i < SystemDirs.size(); ++i) { HSOpts.AddPath(SystemDirs[i], frontend::System, false, false, true); } #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(D)) return ""; bool CanMangle = isa(D) || isa(D) || isa(D) || isa(D); CanMangle = false; if (!CanMangle) return ""; NamedDecl* ND = cast(D); llvm::OwningPtr MC; switch(ABI) { default: llvm_unreachable("Unknown mangling ABI"); break; case CXXABI_Itanium: MC.reset(createItaniumMangleContext(*AST, AST->getDiagnostics())); //AST->setCXXABI(CreateItaniumCXXABI(*AST)); break; case CXXABI_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(ND)) IsDependent = VD->getType()->isDependentType(); if (!MC->shouldMangleDeclName(ND) || IsDependent) return ND->getDeclName().getAsString(); if (const CXXConstructorDecl *CD = dyn_cast(ND)) MC->mangleCXXCtor(CD, Ctor_Base, Out); else if (const CXXDestructorDecl *DD = dyn_cast(ND)) MC->mangleCXXDtor(DD, Dtor_Base, Out); else if (const BlockDecl *BD = dyn_cast(ND)) MC->mangleBlock(BD, 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); } 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(); return GetTagDeclName(Tag->getDecl()); } PrintingPolicy pp(C->getLangOpts()); pp.SuppressTagKeyword = true; std::string TypeName; QualType::getAsStringInternal(Type, Qualifiers(), TypeName, pp); return TypeName; } //-----------------------------------// Cxxi::Class^ Parser::WalkRecordCXX(clang::CXXRecordDecl* Record, bool IsDependent) { using namespace clang; using namespace clix; if (Record->isAnonymousStructOrUnion()) { assert(0); return nullptr; } if (Record->hasFlexibleArrayMember()) { assert(0); return nullptr; } auto NS = GetNamespace(Record); assert(NS && "Expected a valid namespace"); bool isCompleteDefinition = Record->isCompleteDefinition(); auto Name = marshalString(GetTagDeclName(Record)); auto RC = NS->FindClass(Name, isCompleteDefinition, /*Create=*/false); if (RC) return RC; RC = NS->FindClass(Name, isCompleteDefinition, /*Create=*/true); if (!isCompleteDefinition) return RC; RC->IsPOD = Record->isPOD(); RC->IsUnion = Record->isUnion(); RC->IsAbstract = Record->isAbstract(); // Iterate through the record ctors. for(auto it = Record->ctor_begin(); it != Record->ctor_end(); ++it) { CXXMethodDecl* Ctor = (*it); Cxxi::Method^ Method = WalkMethodCXX(Ctor); RC->Methods->Add(Method); } // Iterate through the record methods. for(auto it = Record->method_begin(); it != Record->method_end(); ++it) { CXXMethodDecl* M = (*it); if( isa(M) || isa(M) ) continue; Cxxi::Method^ Method = WalkMethodCXX(M); RC->Methods->Add(Method); } // Get the record layout information. const ASTRecordLayout* Layout = 0; if (!IsDependent) Layout = &C->getASTContext().getASTRecordLayout(Record); // Iterate through the record fields. for(auto it = Record->field_begin(); it != Record->field_end(); ++it) { FieldDecl* FD = (*it); Cxxi::Field^ Field = WalkFieldCXX(FD); if (Layout) Field->Offset = Layout->getFieldOffset(FD->getFieldIndex()); RC->Fields->Add(Field); } //Debug("Size: %I64d\n", Layout.getSize().getQuantity()); return RC; } //-----------------------------------// Cxxi::ClassTemplate^ Parser::WalkClassTemplate(clang::ClassTemplateDecl* TD) { using namespace clang; using namespace clix; auto NS = GetNamespace(TD); auto Class = WalkRecordCXX(TD->getTemplatedDecl(), /*IsDependent*/true); Cxxi::ClassTemplate^ CT = gcnew Cxxi::ClassTemplate(Class); return CT; } //-----------------------------------// Cxxi::FunctionTemplate^ Parser::WalkFunctionTemplate(clang::FunctionTemplateDecl* TD) { using namespace clang; using namespace clix; auto NS = GetNamespace(TD); auto Function = WalkFunction(TD->getTemplatedDecl(), /*IsDependent=*/true); Cxxi::FunctionTemplate^ FT = gcnew Cxxi::FunctionTemplate(Function); return FT; } //-----------------------------------// static Cxxi::CXXMethodKind GetMethodKindFromDecl(clang::DeclarationName Name) { using namespace clang; switch(Name.getNameKind()) { case DeclarationName::Identifier: case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: return Cxxi::CXXMethodKind::Normal; case DeclarationName::CXXConstructorName: return Cxxi::CXXMethodKind::Constructor; case DeclarationName::CXXDestructorName: return Cxxi::CXXMethodKind::Destructor; case DeclarationName::CXXConversionFunctionName: return Cxxi::CXXMethodKind::Conversion; case DeclarationName::CXXOperatorName: case DeclarationName::CXXLiteralOperatorName: return Cxxi::CXXMethodKind::Operator; case DeclarationName::CXXUsingDirective: return Cxxi::CXXMethodKind::UsingDirective; } return Cxxi::CXXMethodKind::Normal; } static Cxxi::CXXOperatorKind GetOperatorKindFromDecl(clang::DeclarationName Name) { using namespace clang; if (Name.getNameKind() != DeclarationName::CXXOperatorName) return Cxxi::CXXOperatorKind::None; switch(Name.getCXXOverloadedOperator()) { #define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ case OO_##Name: return Cxxi::CXXOperatorKind::Name; #include "clang/Basic/OperatorKinds.def" } return Cxxi::CXXOperatorKind::None; } static Cxxi::AccessSpecifier ConvertToAccess(clang::AccessSpecifier AS) { switch(AS) { case clang::AS_private: return Cxxi::AccessSpecifier::Private; case clang::AS_protected: return Cxxi::AccessSpecifier::Protected; case clang::AS_public: return Cxxi::AccessSpecifier::Public; } return Cxxi::AccessSpecifier::Public; } Cxxi::Method^ Parser::WalkMethodCXX(clang::CXXMethodDecl* MD) { using namespace clang; DeclarationName Name = MD->getDeclName(); Cxxi::Method^ Method = gcnew Cxxi::Method(); Method->Access = ConvertToAccess(MD->getAccess()); Method->Kind = GetMethodKindFromDecl(Name); Method->OperatorKind = GetOperatorKindFromDecl(Name); WalkFunction(MD, Method); if (const CXXConstructorDecl* CD = dyn_cast(MD)) { Method->IsDefaultConstructor = CD->isDefaultConstructor(); Method->IsCopyConstructor = CD->isCopyConstructor(); Method->IsMoveConstructor = CD->isMoveConstructor(); } else if (const CXXDestructorDecl* DD = dyn_cast(MD)) { } else if (const CXXConversionDecl* CD = dyn_cast(MD)) { } return Method; } //-----------------------------------// Cxxi::Field^ Parser::WalkFieldCXX(clang::FieldDecl* FD) { using namespace clang; using namespace clix; auto NS = GetNamespace(FD); assert(NS && "Expected a valid namespace"); Cxxi::Field^ F = gcnew Cxxi::Field(); F->Namespace = NS; F->Name = marshalString(FD->getName()); auto TL = FD->getTypeSourceInfo()->getTypeLoc(); F->Type = WalkType(FD->getType(), &TL); F->Access = ConvertToAccess(FD->getAccess()); HandleComments(FD, F); return F; } //-----------------------------------// Cxxi::Namespace^ Parser::GetNamespace(const clang::NamedDecl* ND) { using namespace clang; using namespace clix; SourceLocation Loc = ND->getLocation(); Cxxi::TranslationUnit^ M = GetModule(Loc); // If the declaration is at global scope, just early exit. const DeclContext *Ctx = ND->getDeclContext(); if (Ctx->isTranslationUnit()) return M; // 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 ContextsTy; ContextsTy Contexts; for(; Ctx != nullptr; Ctx = Ctx->getParent()) Contexts.push_back(Ctx); assert(Contexts.back()->isTranslationUnit()); Contexts.pop_back(); Cxxi::Namespace^ NS = M; for (auto I = Contexts.rbegin(), E = Contexts.rend(); I != E; ++I) { const DeclContext* Ctx = *I; switch(Ctx->getDeclKind()) { case Decl::Namespace: { const NamespaceDecl* ND = cast(Ctx); if (ND->isAnonymousNamespace()) continue; auto Name = marshalString(ND->getName()); NS = NS->FindCreateNamespace(Name, NS); break; } case Decl::LinkageSpec: { const LinkageSpecDecl* LD = cast(Ctx); continue; } case Decl::CXXRecord: { // FIXME: Ignore record 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 NS; } static Cxxi::PrimitiveType WalkBuiltinType(const clang::BuiltinType* Builtin) { using namespace Cxxi; 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; } //-----------------------------------// Cxxi::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(); assert(Builtin && "Expected a builtin type"); auto BT = gcnew Cxxi::BuiltinType(); BT->Type = WalkBuiltinType(Builtin); return BT; } case Type::Enum: { auto ET = Type->getAs(); EnumDecl* ED = ET->getDecl(); //auto Name = marshalString(GetTagDeclName(ED)); auto TT = gcnew Cxxi::TagType(); TT->Declaration = WalkDeclaration(ED, 0, /*IgnoreSystemDecls=*/false); return TT; } case Type::Pointer: { auto Pointer = Type->getAs(); auto P = gcnew Cxxi::PointerType(); P->Modifier = Cxxi::PointerType::TypeModifier::Pointer; auto Next = TL->getNextTypeLoc(); P->Pointee = WalkType(Pointer->getPointeeType(), &Next); return P; } case Type::Typedef: { auto TT = Type->getAs(); TypedefNameDecl* TD = TT->getDecl(); auto TTL = TD->getTypeSourceInfo()->getTypeLoc(); auto TDD = safe_cast(WalkDeclaration(TD, &TTL, /*IgnoreSystemDecls=*/false)); auto Type = gcnew Cxxi::TypedefType(); Type->Declaration = TDD; return Type; } case Type::Elaborated: { auto ET = Type->getAs(); auto Next = TL->getNextTypeLoc(); return WalkType(ET->getNamedType(), &Next); } case Type::Record: { auto RT = Type->getAs(); RecordDecl* RD = RT->getDecl(); auto TT = gcnew Cxxi::TagType(); TT->Declaration = WalkDeclaration(RD, 0, /*IgnoreSystemDecls=*/false); return TT; } case Type::Paren: { auto PT = Type->getAs(); auto Next = TL->getNextTypeLoc(); return WalkType(PT->getInnerType(), &Next); } case Type::ConstantArray: { auto AT = AST->getAsConstantArrayType(QualType); auto A = gcnew Cxxi::ArrayType(); auto Next = TL->getNextTypeLoc(); A->Type = WalkType(AT->getElementType(), &Next); A->SizeType = Cxxi::ArrayType::ArraySize::Constant; A->Size = AST->getConstantArrayElementCount(AT); return A; } case Type::FunctionProto: { auto FP = Type->getAs(); auto FTL = dyn_cast(TL); auto RL = FTL->getResultLoc(); auto F = gcnew Cxxi::FunctionType(); F->ReturnType = WalkType(FP->getResultType(), &RL); for (unsigned i = 0; i < FP->getNumArgs(); ++i) { auto FA = gcnew Cxxi::Parameter(); auto PVD = FTL->getArg(i); auto PTL = PVD->getTypeSourceInfo()->getTypeLoc(); FA->Name = marshalString(PVD->getNameAsString()); FA->Type = WalkType(PVD->getType(), &PTL); F->Arguments->Add(FA); } return F; } case Type::TypeOf: { auto TO = Type->getAs(); return WalkType(TO->getUnderlyingType()); } case Type::TypeOfExpr: { auto TO = Type->getAs(); return WalkType(TO->getUnderlyingExpr()->getType()); } case Type::MemberPointer: { auto MP = Type->getAs(); auto Next = TL->getNextTypeLoc(); auto MPT = gcnew Cxxi::MemberPointerType(); MPT->Pointee = WalkType(MP->getPointeeType(), &Next); return MPT; } case Type::TemplateSpecialization: { auto TS = Type->getAs(); auto TST = gcnew Cxxi::TemplateSpecializationType(); TemplateName Name = TS->getTemplateName(); TST->Template = safe_cast(WalkDeclaration( Name.getAsTemplateDecl(), 0, /*IgnoreSystemDecls=*/false)); clang::TypeLoc::TypeLocClass Class = TL->getTypeLocClass(); int Loc = (int) Class; auto TSTL = dyn_cast(TL); for (unsigned I = 0, E = TS->getNumArgs(); I != E; ++I) { const TemplateArgument& TA = TS->getArg(I); auto Arg = Cxxi::TemplateArgument(); TemplateArgumentLoc ArgLoc; if (Class == clang::TypeLoc::TemplateSpecialization) ArgLoc = TSTL->getArgLoc(I); switch(TA.getKind()) { case TemplateArgument::Type: { Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::Type; TypeLoc ArgTL; if (Class == clang::TypeLoc::TemplateSpecialization) ArgTL = ArgLoc.getTypeSourceInfo()->getTypeLoc(); Arg.Type = WalkType(TA.getAsType(), &ArgTL); break; } case TemplateArgument::Declaration: Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::Declaration; Arg.Declaration = WalkDeclaration(TA.getAsDecl(), 0); break; case TemplateArgument::NullPtr: Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::NullPtr; break; case TemplateArgument::Integral: Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::Integral; //Arg.Type = WalkType(TA.getIntegralType(), 0); Arg.Integral = TA.getAsIntegral().getLimitedValue(); break; case TemplateArgument::Template: Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::Template; break; case TemplateArgument::TemplateExpansion: Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::TemplateExpansion; break; case TemplateArgument::Expression: Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::Expression; break; case TemplateArgument::Pack: Arg.Kind = Cxxi::TemplateArgument::ArgumentKind::Pack; break; } TST->Arguments->Add(Arg); } return TST; } case Type::TemplateTypeParm: { auto TP = Type->getAs(); auto TPT = gcnew Cxxi::TemplateParameterType(); //TPT->Parameter = WalkDeclaration(TP->getDecl()); return TPT; } case Type::InjectedClassName: { auto IN = Type->getAs(); return nullptr; } case Type::DependentName: { auto DN = Type->getAs(); return nullptr; } case Type::LValueReference: { auto LR = Type->getAs(); auto P = gcnew Cxxi::PointerType(); P->Modifier = Cxxi::PointerType::TypeModifier::LVReference; TypeLoc Next; if (!TL->isNull()) Next = TL->getNextTypeLoc(); P->Pointee = WalkType(LR->getPointeeType(), &Next); return P; } case Type::RValueReference: { auto LR = Type->getAs(); auto P = gcnew Cxxi::PointerType(); P->Modifier = Cxxi::PointerType::TypeModifier::RVReference; TypeLoc Next; if (!TL->isNull()) Next = TL->getNextTypeLoc(); P->Pointee = WalkType(LR->getPointeeType(), &Next); return P; } default: { Debug("Unhandled type class '%s'\n", Type->getTypeClassName()); return nullptr; } } } //-----------------------------------// Cxxi::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(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 |= Cxxi::Enumeration::EnumModifiers::Scoped; // Get the underlying integer backing the enum. QualType IntType = ED->getIntegerType(); E->Type = WalkType(IntType, 0); E->BuiltinType = safe_cast(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 Cxxi::Enumeration::Item(); EnumItem->Name = marshalString(ECD->getNameAsString()); EnumItem->Value = (int) ECD->getInitVal().getLimitedValue(); EnumItem->Comment = marshalString(BriefText); //EnumItem->ExplicitValue = ECD->getExplicitValue(); E->AddItem(EnumItem); } return E; } //-----------------------------------// static Cxxi::CallingConvention ConvertCallConv(clang::CallingConv CC) { using namespace clang; switch(CC) { case CC_Default: case CC_C: return Cxxi::CallingConvention::C; case CC_X86StdCall: return Cxxi::CallingConvention::StdCall; case CC_X86FastCall: return Cxxi::CallingConvention::FastCall; case CC_X86ThisCall: return Cxxi::CallingConvention::ThisCall; case CC_X86Pascal: case CC_AAPCS: case CC_AAPCS_VFP: return Cxxi::CallingConvention::Unknown; } return Cxxi::CallingConvention::Default; } void Parser::WalkFunction(clang::FunctionDecl* FD, Cxxi::Function^ F, bool IsDependent) { using namespace clang; using namespace clix; auto FT = FD->getType()->getAs(); auto CC = FT->getCallConv(); auto NS = GetNamespace(FD); assert(NS && "Expected a valid namespace"); F->Name = marshalString(FD->getNameAsString()); F->Namespace = NS; F->IsVariadic = FD->isVariadic(); F->IsInline = FD->isInlined(); F->CallingConvention = ConvertCallConv(CC); TypeLoc RTL; if (auto TSI = FD->getTypeSourceInfo()) { TypeLoc TL = TSI->getTypeLoc(); RTL = ((FunctionTypeLoc*) &TL)->getResultLoc(); } F->ReturnType = WalkType(FD->getResultType(), &RTL); String Mangled = GetDeclMangledName(FD, CXXABI_Microsoft, IsDependent); F->Mangled = marshalString(Mangled); for(auto it = FD->param_begin(); it != FD->param_end(); ++it) { ParmVarDecl* VD = (*it); auto P = gcnew Cxxi::Parameter(); P->Name = marshalString(VD->getNameAsString()); P->IsConst = VD->getType().isConstQualified(); TypeLoc PTL; if (auto TSI = VD->getTypeSourceInfo()) PTL = VD->getTypeSourceInfo()->getTypeLoc(); P->Type = WalkType(VD->getType(), &PTL); P->HasDefaultValue = VD->hasDefaultArg(); F->Parameters->Add(P); } } Cxxi::Function^ Parser::WalkFunction(clang::FunctionDecl* FD, bool IsDependent) { using namespace clang; using namespace clix; auto NS = GetNamespace(FD); assert(NS && "Expected a valid namespace"); auto Name = marshalString(FD->getNameAsString()); Cxxi::Function^ F = NS->FindFunction(Name, /*Create=*/ false); if (F != nullptr) return F; F = gcnew Cxxi::Function(); WalkFunction(FD, F, IsDependent); NS->Functions->Add(F); return F; } //-----------------------------------// static bool IsUserLocation(clang::SourceManager& SM, clang::SourceLocation Loc) { auto Kind = SM.getFileCharacteristic(Loc); return Kind == clang::SrcMgr::C_User; } bool Parser::IsValidDeclaration(const clang::SourceLocation& Loc) { using namespace clang; SourceManager& SM = C->getSourceManager(); PresumedLoc PLoc = SM.getPresumedLoc(Loc); // Igore built in declarations. if(PLoc.isInvalid() || !strcmp(PLoc.getFilename(), "")) return false; // Also ignore declarations that come from system headers. if (!IsUserLocation(SM, Loc)) return false; return true; } //-----------------------------------// 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); } } //-----------------------------------// Cxxi::TranslationUnit^ Parser::GetModule(clang::SourceLocation Loc) { using namespace clang; using namespace clix; SourceManager& SM = C->getSourceManager(); if (Loc.isMacroID()) Loc = SM.getExpansionLoc(Loc); StringRef File = SM.getFilename(Loc); if (!File.data() || File.empty()) { assert(0 && "Expected to find a valid file"); return nullptr; } auto Unit = Lib->FindOrCreateModule(marshalString(File)); Unit->IsSystemHeader = SM.isInSystemHeader(Loc); return Unit; } //-----------------------------------// 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(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 (!IsUserLocation(SM, 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 Cxxi::MacroDefinition(); macro->Name = marshalString(II->getName())->Trim(); macro->Expression = marshalString(Expression)->Trim(); auto M = GetModule(BeginExpr); M->Macros->Add(macro); break; } default: break; } } } //-----------------------------------// Cxxi::Variable^ Parser::WalkVariable(clang::VarDecl *VD) { using namespace clang; using namespace clix; auto Var = gcnew Cxxi::Variable(); Var->Name = marshalString(VD->getName()); return Var; } //-----------------------------------// void Parser::HandleComments(clang::Decl* D, Cxxi::Declaration^ Decl) { using namespace clang; using namespace clix; // Get the declaration comment. std::string BriefText; if (const RawComment* Comment = AST->getRawCommentForAnyRedecl(D)) BriefText = Comment->getBriefText(*AST); Decl->BriefComment = marshalString(BriefText); SourceManager& SM = C->getSourceManager(); const LangOptions& LangOpts = C->getLangOpts(); auto Range = CharSourceRange::getTokenRange(D->getSourceRange()); bool Invalid; StringRef DeclText = Lexer::getSourceText(Range, SM, LangOpts, &Invalid); //assert(!Invalid && "Should have a valid location"); if (!Invalid) Decl->DebugText = marshalString(DeclText); } //-----------------------------------// Cxxi::Declaration^ Parser::WalkDeclarationDef(clang::Decl* D) { return WalkDeclaration(D, 0, /*IgnoreSystemDecls=*/true, /*CanBeDefinition=*/true); } Cxxi::Declaration^ Parser::WalkDeclaration(clang::Decl* D, clang::TypeLoc* TL, 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(Attr); assert(Annotation != nullptr); StringRef AnnotationText = Annotation->getAnnotation(); } Cxxi::Declaration^ Decl; auto Kind = D->getKind(); switch(D->getKind()) { case Decl::CXXRecord: { CXXRecordDecl* RD = cast(D); auto Class = WalkRecordCXX(RD); HandleComments(RD, Class); // 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++; } Decl = Class; break; } case Decl::ClassTemplate: { ClassTemplateDecl* TD = cast(D); auto Template = WalkClassTemplate(TD); auto NS = GetNamespace(TD); Template->Namespace = NS; NS->Templates->Add(Template); Decl = Template; break; } case Decl::FunctionTemplate: { FunctionTemplateDecl* TD = cast(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(D); auto E = WalkEnum(ED); HandleComments(ED, E); Decl = E; break; } case Decl::Function: { FunctionDecl* FD = cast(D); if (!FD->isFirstDeclaration()) break; auto F = WalkFunction(FD); HandleComments(FD, F); Decl = F; break; } case Decl::LinkageSpec: { LinkageSpecDecl* LS = cast(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(D); auto NS = GetNamespace(TD); auto Name = marshalString(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->Type = WalkType(TD->getUnderlyingType(), &TTL); Decl = Typedef; break; } case Decl::Namespace: { NamespaceDecl* ND = cast(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(D); auto V = WalkVariable(VD); HandleComments(VD, V); Decl = V; break; } default: { Debug("Unhandled declaration kind: %s\n", D->getDeclKindName()); break; } }; return Decl; } //-----------------------------------// struct ParseConsumer : public clang::ASTConsumer { virtual ~ParseConsumer() { } virtual bool HandleTopLevelDecl(clang::DeclGroupRef) { return true; } }; bool Parser::Parse(const std::string& File) { if (File.empty()) return false; C->setASTConsumer(new ParseConsumer()); // 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->getPreprocessor(), &C->getASTConsumer(), C->getASTContext(), /*PrintStats=*/false, clang::TU_Complete, 0, /*SkipFunctionBodies=*/true); client->EndSourceFile(); if(client->getNumErrors() != 0) { // We had some errors while parsing the file. // Report this... return false; } AST = &C->getASTContext(); WalkAST(); return true; }