Browse Source

Added C++/CLI wrapper around the C++ Clang parser.

pull/1/head
triton 13 years ago
parent
commit
c4fd9ae614
  1. 48
      src/Parser/CXXABI.h
  2. 203
      src/Parser/Interop.h
  3. 25
      src/Parser/Main.cpp
  4. 897
      src/Parser/Parser.cpp
  5. 80
      src/Parser/Parser.h

48
src/Parser/CXXABI.h

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
//===----- CXXABI.h - Interface to C++ ABIs ---------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This provides an abstract class for C++ AST support. Concrete
// subclasses of this implement AST support for specific C++ ABIs.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_CXXABI_H
#define LLVM_CLANG_AST_CXXABI_H
#include "clang/AST/Type.h"
namespace clang {
class ASTContext;
class MemberPointerType;
/// Implements C++ ABI-specific semantic analysis functions.
class CXXABI {
public:
virtual ~CXXABI();
/// Returns the size of a member pointer in multiples of the target
/// pointer size.
virtual unsigned getMemberPointerSize(const MemberPointerType *MPT) const = 0;
/// Returns the default calling convention for C++ methods.
virtual CallingConv getDefaultMethodCallConv() const = 0;
// Returns whether the given class is nearly empty, with just virtual pointers
// and no data except possibly virtual bases.
virtual bool isNearlyEmpty(const CXXRecordDecl *RD) const = 0;
};
/// Creates an instance of a C++ ABI class.
CXXABI *CreateARMCXXABI(ASTContext &Ctx);
CXXABI *CreateItaniumCXXABI(ASTContext &Ctx);
CXXABI *CreateMicrosoftCXXABI(ASTContext &Ctx);
}
#endif

203
src/Parser/Interop.h

@ -0,0 +1,203 @@ @@ -0,0 +1,203 @@
// ------------------------------------------------------------------------------------------- //
// clix.hpp (from http://blog.nuclex-games.com/mono-dotnet/cxx-cli-string-marshaling)
//
// Marshals strings between .NET and C++ using C++/CLI (Visual C++ 2005 and later only).
// Faster and cleaner than the System::Interop method because it uses garbage collected memory.
// Use at your own leisure. No warranties whatsoever provided.
//
// Original code by Markus Ewald
// Updated version including several improvements suggested by Neil Hunt
//
// Licensed under the IBM CPL (free of charge, closed source commercial use is okay)
// ------------------------------------------------------------------------------------------- //
#pragma once
#include <string>
#include <vcclr.h>
// CLI extensions namespace
namespace clix {
/// <summary>Encoding types for strings</summary>
enum Encoding {
/// <summary>ANSI encoding</summary>
/// <remarks>
/// This is the default encoding you've most likely been using all around in C++. ANSI
/// means 8 Bit encoding with character codes depending on the system's selected code page.
/// <remarks>
E_ANSI,
/// <summary>UTF-8 encoding</summary>
/// <remarks>
/// This is the encoding commonly used for multilingual C++ strings. All ASCII characters
/// (0-127) will be represented as single bytes. Be aware that UTF-8 uses more than one
/// byte for extended characters, so std::string::length() might not reflect the actual
/// length of the string in characters if it contains any non-ASCII characters.
/// <remarks>
E_UTF8,
/// <summary>UTF-16 encoding</summary>
/// <remarks>
/// This is the suggested to be used for marshaling and the native encoding of .NET
/// strings. It is similar to UTF-8 but uses a minimum of two bytes per character, making
/// the number of bytes required for a given string better predictable. Be aware, however,
/// that UTF-16 can still use more than two bytes for a character, so std::wstring::length()
/// might not reflect the actual length of the string.
/// </remarks>
E_UTF16, E_UNICODE = E_UTF16
};
// Ignore this if you're just scanning the headers for informations!
/* All this template stuff might seem like overkill, but it is well thought out and enables
you to use a readable and convenient call while still keeping the highest possible code
efficiency due to compile-time evaluation of the required conversion path.
*/
namespace detail {
// Get C++ string type for specified encoding
template<Encoding encoding> struct StringTypeSelector;
template<> struct StringTypeSelector<E_ANSI> { typedef std::string Type; };
template<> struct StringTypeSelector<E_UTF8> { typedef std::string Type; };
template<> struct StringTypeSelector<E_UTF16> { typedef std::wstring Type; };
// Compile-time selection depending on whether a string is managed
template<typename StringType> struct IfManaged {
struct Select {
template<typename TrueType, typename FalseType>
struct Either { typedef FalseType Type; };
};
enum { Result = false };
};
template<> struct IfManaged<System::String ^> {
struct Select {
template<typename TrueType, typename FalseType>
struct Either { typedef TrueType Type; };
};
enum { Result = true };
};
// Direction of the marshaling process
enum MarshalingDirection {
CxxFromNet,
NetFromCxx
};
// The actual marshaling code
template<MarshalingDirection direction> struct StringMarshaler;
// Marshals to .NET from C++ strings
template<> struct StringMarshaler<NetFromCxx> {
template<Encoding encoding, typename SourceType>
static System::String ^marshal(const SourceType &string) {
// Constructs a std::[w]string in case someone gave us a char * to choke on
return marshalCxxString<encoding, SourceType>(string);
}
template<Encoding encoding, typename SourceType>
static System::String ^marshalCxxString(
const typename StringTypeSelector<encoding>::Type &cxxString
) {
typedef typename StringTypeSelector<encoding>::Type SourceStringType;
size_t byteCount = cxxString.length() * sizeof(SourceStringType::value_type);
if(byteCount == 0) return System::String::Empty;
// Copy the C++ string contents into a managed array of bytes
array<unsigned char> ^bytes = gcnew array<unsigned char>(byteCount);
{ pin_ptr<unsigned char> pinnedBytes = &bytes[0];
memcpy(pinnedBytes, cxxString.c_str(), byteCount);
}
// Now let one of .NET's encoding classes do the rest
return decode<encoding>(bytes);
}
private:
// Converts a byte array based on the selected encoding
template<Encoding encoding> static System::String ^decode(array<unsigned char> ^bytes);
template<> static System::String ^decode<E_ANSI>(array<unsigned char> ^bytes) {
return System::Text::Encoding::Default->GetString(bytes);
}
template<> static System::String ^decode<E_UTF8>(array<unsigned char> ^bytes) {
return System::Text::Encoding::UTF8->GetString(bytes);
}
template<> static System::String ^decode<E_UTF16>(array<unsigned char> ^bytes) {
return System::Text::Encoding::Unicode->GetString(bytes);
}
};
// Marshals to C++ strings from .NET
template<> struct StringMarshaler<CxxFromNet> {
template<Encoding encoding, typename SourceType>
static typename detail::StringTypeSelector<encoding>::Type marshal(
System::String ^string
) {
typedef typename StringTypeSelector<encoding>::Type StringType;
// First, we use .NET's encoding classes to convert the string into a byte array
array<unsigned char> ^bytes = encode<encoding>(string);
if(bytes->Length == 0) return StringType();
// Then we construct our native string from that byte array
pin_ptr<unsigned char> pinnedBytes(&bytes[0]);
return StringType(
reinterpret_cast<StringType::value_type *>(static_cast<unsigned char *>(pinnedBytes)),
bytes->Length / sizeof(StringType::value_type)
);
}
template<> static std::wstring marshal<E_UTF16, System::String ^>(
System::String ^string
) {
// We can directly accesss the characters in the managed string
pin_ptr<const wchar_t> pinnedChars(::PtrToStringChars(string));
return std::wstring(pinnedChars, string->Length);
}
private:
// Converts a string based on the selected encoding
template<Encoding encoding> static array<unsigned char> ^encode(System::String ^string);
template<> static array<unsigned char> ^encode<E_ANSI>(System::String ^string) {
return System::Text::Encoding::Default->GetBytes(string);
}
template<> static array<unsigned char> ^encode<E_UTF8>(System::String ^string) {
return System::Text::Encoding::UTF8->GetBytes(string);
}
template<> static array<unsigned char> ^encode<E_UTF16>(System::String ^string) {
return System::Text::Encoding::Unicode->GetBytes(string);
}
};
} // namespace detail
// ----------------------------------------------------------------------------------------- //
// clix::marshalString()
// ----------------------------------------------------------------------------------------- //
/// <summary>Marshals strings between .NET managed and C++ native</summary>
/// <remarks>
/// This all-in-one function marshals native C++ strings to .NET strings and vice versa.
/// You have to specify an encoding to use for the conversion, which always applies to the
/// native C++ string as .NET always uses UTF-16 for its own strings.
/// </remarks>
/// <param name="string">String to be marshalled to the other side</param>
/// <returns>The marshaled representation of the string</returns>
template<Encoding encoding, typename SourceType>
typename detail::IfManaged<SourceType>::Select::Either<
typename detail::StringTypeSelector<encoding>::Type,
System::String ^
>::Type marshalString(SourceType string) {
// Pass on the call to our nifty template routines
return detail::StringMarshaler<
detail::IfManaged<SourceType>::Result ? detail::CxxFromNet : detail::NetFromCxx
>::marshal<encoding, SourceType>(string);
}
} // namespace clix

25
src/Parser/Main.cpp

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
/************************************************************************
*
* Flush3D <http://www.flush3d.com> © (2008-201x)
* Licensed under the LGPL 2.1 (GNU Lesser General Public License)
*
************************************************************************/
#include "Parser.h"
#include "Interop.h"
public ref class ClangParser
{
public:
static bool Parse(ParserOptions^ Opts)
{
if (!Opts->FileName) return false;
using namespace clix;
std::string File = marshalString<E_UTF8>(Opts->FileName);
Parser p(Opts);
return p.Parse(File);
}
};

897
src/Parser/Parser.cpp

@ -0,0 +1,897 @@ @@ -0,0 +1,897 @@
/************************************************************************
*
* Flush3D <http://www.flush3d.com> © (2008-201x)
* Licensed under the LGPL 2.1 (GNU Lesser General Public License)
*
************************************************************************/
#include "Parser.h"
#include <llvm/Support/Path.h>
#include <clang/Basic/Version.h>
#include <clang/Config/config.h>
#include "clang/AST/ASTContext.h"
#include "clang/Lex/HeaderSearch.h"
#include <clang/Lex/PreprocessingRecord.h>
#include "clang/Frontend/HeaderSearchOptions.h"
#include <clang/Frontend/Utils.h>
#include <clang/Driver/Util.h>
#include "Interop.h"
#include <string>
//-----------------------------------//
Parser::Parser(ParserOptions^ Opts) : Lib(Opts->Library)
{
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;
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;
// 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<std::string> 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(false /*RecordConditionals*/);
P.getBuiltinInfo().InitializeBuiltins(P.getIdentifierTable(), P.getLangOpts());
}
}
//-----------------------------------//
std::string Parser::GetDeclMangledName(clang::Decl* D, clang::TargetCXXABI ABI)
{
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)
{
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 (!MC->shouldMangleDeclName(ND))
{
IdentifierInfo *II = ND->getIdentifier();
return II->getName();
}
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 if (const BlockDecl *BD = dyn_cast<BlockDecl>(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 GetTagDeclName(const clang::TagDecl* D)
{
using namespace clang;
if (const IdentifierInfo *II = D->getIdentifier())
return II->getName();
else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl())
{
assert(Typedef->getIdentifier() && "Typedef without identifier?");
return Typedef->getIdentifier()->getName();
}
else
return D->getNameAsString();
assert(0 && "Expected to have a name");
return std::string();
}
std::string Parser::GetTypeBindName(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;
//pp.SuppressSpecifiers = true;
std::string TypeName;
QualType::getAsStringInternal(Type, Qualifiers(), TypeName, pp);
return std::string();
}
//-----------------------------------//
Cxxi::Class^ Parser::WalkRecordCXX(clang::CXXRecordDecl* Record)
{
using namespace clang;
using namespace clix;
if (Record->isAnonymousStructOrUnion())
return nullptr;
auto RC = gcnew Cxxi::Class();
RC->Name = marshalString<E_UTF8>(GetTagDeclName(Record));
RC->IsPOD = Record->isPOD();
// 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<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M) )
continue;
Cxxi::Method^ Method = WalkMethodCXX(M);
RC->Methods->Add(Method);
}
// Iterate through the record fields.
for(auto it = Record->field_begin(); it != Record->field_end(); ++it)
{
FieldDecl* FD = (*it);
Cxxi::Field^ Field = WalkFieldCXX(FD);
RC->Fields->Add(Field);
}
// Get the record layout information.
const ASTRecordLayout& Layout = C->getASTContext().getASTRecordLayout(Record);
//Debug("Size: %I64d\n", Layout.getSize().getQuantity());
return RC;
}
//-----------------------------------//
Cxxi::Method^ Parser::WalkMethodCXX(clang::CXXMethodDecl* Method)
{
using namespace clang;
DeclarationName MethodName = Method->getDeclName();
Debug("Method: %s\n", MethodName.getAsString().c_str());
// Write the return type.
QualType Return = Method->getResultType();
for(auto it = Method->param_begin(); it != Method->param_end(); ++it)
{
ParmVarDecl* Parm = (*it);
QualType ParmType = Parm->getType();
std::string ParmTypeName = GetTypeBindName(ParmType.getTypePtr());
Debug("\tParameter: %s %s\n",
ParmTypeName.c_str(), Parm->getName().str().c_str());
}
std::string Mangled = GetDeclMangledName(Method, CXXABI_Microsoft);
Debug("\tMangling: %s\n", Mangled.c_str());
return nullptr;
}
//-----------------------------------//
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::Field^ Parser::WalkFieldCXX(clang::FieldDecl* FD)
{
using namespace clang;
using namespace clix;
Cxxi::Field^ F = gcnew Cxxi::Field();
F->Name = marshalString<E_UTF8>(FD->getName());
F->Type = ConvertTypeToCLR(FD->getType());
F->Access = ConvertToAccess(FD->getAccess());
HandleComments(FD, F);
return F;
}
//-----------------------------------//
static Cxxi::PrimitiveType ConvertBuiltinTypeToCLR(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::ConvertTypeToCLR(clang::QualType QualType)
{
using namespace clang;
using namespace clix;
if (QualType.isNull())
return nullptr;
clang::QualType Desugared = QualType.getDesugaredType(*AST);
if (Desugared.isNull())
return nullptr;
const clang::Type* Type = QualType.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 Cxxi::BuiltinType();
BT->Type = ConvertBuiltinTypeToCLR(Builtin);
return BT;
}
case Type::Enum:
{
auto ET = Type->getAs<clang::EnumType>();
const EnumDecl* ED = ET->getDecl();
// Assume that the type has already been defined for now.
String Name(GetTagDeclName(ED));
auto E = Lib->FindEnum(marshalString<E_UTF8>(Name));
auto TT = gcnew Cxxi::TagType();
TT->Declaration = E;
return TT;
}
case Type::Pointer:
{
auto Pointer = Type->getAs<clang::PointerType>();
auto P = gcnew Cxxi::PointerType();
P->Modifier = Cxxi::PointerType::TypeModifier::Pointer;
P->Pointee = ConvertTypeToCLR(Pointer->getPointeeType());
return P;
}
case Type::Typedef:
{
auto TT = Type->getAs<clang::TypedefType>();
const TypedefNameDecl* TND = TT->getDecl();
return ConvertTypeToCLR(TND->getUnderlyingType());
}
case Type::Elaborated:
{
auto ET = Type->getAs<clang::ElaboratedType>();
return ConvertTypeToCLR(ET->getNamedType());
}
case Type::Record:
{
auto RT = Type->getAs<clang::RecordType>();
const RecordDecl* RD = RT->getDecl();
// Assume that the type has already been defined for now.
String Name(GetTagDeclName(RD));
auto D = Lib->FindClass(marshalString<E_UTF8>(Name));
auto TT = gcnew Cxxi::TagType();
TT->Declaration = D;
return TT;
}
case Type::Paren:
{
auto PT = Type->getAs<clang::ParenType>();
return ConvertTypeToCLR(PT->getInnerType());
}
case Type::ConstantArray:
{
auto AT = AST->getAsConstantArrayType(QualType);
auto A = gcnew Cxxi::ArrayType();
A->Type = ConvertTypeToCLR(AT->getElementType());
A->SizeType = Cxxi::ArrayType::ArraySize::Constant;
A->Size = AST->getConstantArrayElementCount(AT);
return A;
}
case Type::FunctionProto:
{
auto FP = Type->getAs<clang::FunctionProtoType>();
auto F = gcnew Cxxi::FunctionType();
F->ReturnType = ConvertTypeToCLR(FP->getResultType());
return F;
}
case Type::TypeOf:
{
auto TO = Type->getAs<clang::TypeOfType>();
return ConvertTypeToCLR(TO->getUnderlyingType());
}
case Type::TypeOfExpr:
{
auto TO = Type->getAs<clang::TypeOfExprType>();
return ConvertTypeToCLR(TO->getUnderlyingExpr()->getType());
}
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 E = gcnew Cxxi::Enumeration();
E->Name = marshalString<E_UTF8>(GetTagDeclName(ED));
if (ED->isScoped())
E->Modifiers |= Cxxi::Enumeration::EnumModifiers::Scoped;
// Get the underlying integer backing the enum.
QualType IntType = ED->getIntegerType();
E->Type = safe_cast<Cxxi::BuiltinType^>(ConvertTypeToCLR(IntType));
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<E_UTF8>(ECD->getNameAsString());
EnumItem->Value = (int) ECD->getInitVal().getLimitedValue();
EnumItem->Comment = marshalString<E_UTF8>(BriefText);
//EnumItem->ExplicitValue = ECD->getExplicitValue();
E->AddItem(EnumItem);
}
return E;
}
//-----------------------------------//
Cxxi::Function^ Parser::WalkFunction(clang::FunctionDecl* FD)
{
using namespace clang;
using namespace clix;
auto F = gcnew Cxxi::Function();
F->Name = marshalString<E_UTF8>(FD->getNameAsString());
F->IsVariadic = FD->isVariadic();
F->IsInline = FD->isInlined();
F->CallingConvention = Cxxi::CallingConvention::Default;
F->ReturnType = ConvertTypeToCLR(FD->getResultType());
for(auto it = FD->param_begin(); it != FD->param_end(); ++it)
{
ParmVarDecl* VD = (*it);
auto P = gcnew Cxxi::Parameter();
P->Name = marshalString<E_UTF8>(VD->getNameAsString());
P->Type = ConvertTypeToCLR(VD->getType());
P->HasDefaultValue = VD->hasDefaultArg();
F->Parameters->Add(P);
}
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(), "<built-in>"))
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();
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);
WalkDeclaration(D);
}
}
//-----------------------------------//
Cxxi::Module^ Parser::GetModule(clang::SourceLocation Loc)
{
using namespace clang;
using namespace clix;
SourceManager& SM = C->getSourceManager();
StringRef File = SM.getFilename(Loc);
return Lib->FindOrCreateModule(marshalString<E_UTF8>(File));
}
//-----------------------------------//
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 (!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::MacroDefine();
macro->Name = marshalString<E_UTF8>(II->getName())->Trim();
macro->Expression = marshalString<E_UTF8>(Expression)->Trim();
auto M = GetModule(BeginExpr);
M->Macros->Add(macro);
break;
}
default: break;
}
}
}
//-----------------------------------//
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<E_UTF8>(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");
Decl->DebugText = marshalString<E_UTF8>(DeclText);
}
//-----------------------------------//
void Parser::WalkDeclaration(clang::Decl* D)
{
using namespace clang;
if (!IsValidDeclaration(D->getLocation()))
return;
if(NamedDecl* ND = dyn_cast<NamedDecl>(D))
{
const char* KindName = D->getDeclKindName();
DeclarationName DN = ND->getDeclName();
std::string DeclName = DN.getAsString();
//Debug("(%s) %s\n", KindName, DeclName.c_str());
std::string Mangled = GetDeclMangledName(ND, CXXABI_Microsoft);
}
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();
}
using namespace clix;
switch(D->getKind())
{
case Decl::CXXRecord:
{
CXXRecordDecl* RD = cast<CXXRecordDecl>(D);
if (!RD->isThisDeclarationADefinition())
break;
auto Class = WalkRecordCXX(RD);
HandleComments(RD, Class);
auto M = GetModule(RD->getLocation());
M->Global->Classes->Add(Class);
break;
}
case Decl::Enum:
{
EnumDecl* ED = cast<EnumDecl>(D);
if (!ED->isThisDeclarationADefinition())
break;
auto E = WalkEnum(ED);
HandleComments(ED, E);
auto M = GetModule(ED->getLocation());
M->Global->Enums->Add(E);
break;
}
case Decl::Function:
{
FunctionDecl* FD = cast<FunctionDecl>(D);
if (!FD->isFirstDeclaration())
break;
auto F = WalkFunction(FD);
HandleComments(FD, F);
auto M = GetModule(FD->getLocation());
M->Global->Functions->Add(F);
break;
}
case Decl::LinkageSpec:
{
LinkageSpecDecl* LS = cast<LinkageSpecDecl>(D);
for (auto it = LS->decls_begin(); it != LS->decls_end(); ++it)
{
Decl* D = (*it);
WalkDeclaration(D);
}
break;
}
case Decl::Typedef:
{
TypedefDecl* TD = cast<TypedefDecl>(D);
const QualType& Type = TD->getUnderlyingType();
std::string Name = GetTypeBindName(Type.getTypePtr());
if (Type->isBuiltinType())
break;
#if 0
// If we have a type name for a previously unnamed type,
// then use this name as the name of the original type.
if(auto CType = ConvertTypeToCLR(Type))
CType->Name = marshalString<E_UTF8>(Name);
#endif
break;
}
case Decl::Namespace:
{
NamespaceDecl* ND = cast<NamespaceDecl>(D);
for (auto it = ND->decls_begin(); it != ND->decls_end(); ++it)
{
Decl* D = (*it);
WalkDeclaration(D);
}
break;
}
default:
{
Debug("Unhandled declaration kind: %s\n", D->getDeclKindName());
//assert(0 && "Unhandled declaration kind");
break;
} };
}
//-----------------------------------//
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());
// Get the file from the file system
const clang::FileEntry* file = C->getFileManager().getFile(File.c_str());
if (!file)
{
Debug("Filename '%s' was not found."
"Check your compiler paths.\n", File.c_str());
return false;
}
C->getSourceManager().createMainFileID(file);
clang::DiagnosticConsumer* client = C->getDiagnostics().getClient();
client->BeginSourceFile(C->getLangOpts(), &C->getPreprocessor());
ParseAST(C->getPreprocessor(), &C->getASTConsumer(), C->getASTContext());
client->EndSourceFile();
if(client->getNumErrors() != 0)
{
// We had some errors while parsing the file.
// Report this...
return false;
}
AST = &C->getASTContext();
WalkAST();
return true;
}

80
src/Parser/Parser.h

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
/************************************************************************
*
* Flush3D <http://www.flush3d.com> © (2008-201x)
* Licensed under the LGPL 2.1 (GNU Lesser General Public License)
*
************************************************************************/
#include <llvm/Support/Host.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Frontend/ASTConsumers.h>
#include <clang/Basic/FileManager.h>
#include <clang/Basic/TargetOptions.h>
#include <clang/Basic/TargetInfo.h>
#include <clang/Basic/IdentifierTable.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/Mangle.h>
#include <clang/AST/RecordLayout.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Parse/ParseAST.h>
#include <clang/Sema/Sema.h>
#include "CXXABI.h"
#include <string>
#include <cstdarg>
#using <Bridge.dll>
#include <vcclr.h>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define Debug printf
public ref struct ParserOptions
{
// C/C++ header file name.
System::String^ FileName;
Cxxi::Library^ Library;
bool Verbose;
};
struct Parser
{
Parser(ParserOptions^ Opts);
void Setup(ParserOptions^ Opts);
bool Parse(const std::string& File);
protected:
// AST traversers
void WalkAST();
void WalkDeclaration(clang::Decl* D);
void WalkMacros(clang::PreprocessingRecord* PR);
Cxxi::Enumeration^ WalkEnum(clang::EnumDecl*);
Cxxi::Function^ WalkFunction(clang::FunctionDecl*);
Cxxi::Class^ WalkRecordCXX(clang::CXXRecordDecl*);
Cxxi::Method^ WalkMethodCXX(clang::CXXMethodDecl*);
Cxxi::Field^ WalkFieldCXX(clang::FieldDecl*);
// Clang helpers
bool IsValidDeclaration(const clang::SourceLocation& Loc);
std::string GetDeclMangledName(clang::Decl*, clang::TargetCXXABI);
std::string GetTypeBindName(const clang::Type*);
Cxxi::Module^ GetModule(clang::SourceLocation Loc);
Cxxi::Type^ ConvertTypeToCLR(clang::QualType QualType);
void HandleComments(clang::Decl* D, Cxxi::Declaration^);
gcroot<Cxxi::Library^> Lib;
llvm::OwningPtr<clang::CompilerInstance> C;
clang::ASTContext* AST;
};
//-----------------------------------//
typedef std::string String;
String StringFormatArgs(const char* str, va_list args);
String StringFormat(const char* str, ...);
Loading…
Cancel
Save