From 66ccc6937cd8ec6eb4599d91c19144f7d3bbb6ec Mon Sep 17 00:00:00 2001 From: Gnimuc Date: Sun, 27 Oct 2024 23:33:34 +0900 Subject: [PATCH] Add libclang-style C API - Add `CXScope` and `CXQualType` as a bridge between libclang types and CppInterOp's types - Add C API for interpreter and scope manipulations --- include/clang-c/CXCppInterOp.h | 366 +++++++++++ lib/Interpreter/CMakeLists.txt | 1 + lib/Interpreter/CXCppInterOp.cpp | 627 +++++++++++++++++++ unittests/CppInterOp/InterpreterTest.cpp | 15 + unittests/CppInterOp/ScopeReflectionTest.cpp | 12 + unittests/CppInterOp/TypeReflectionTest.cpp | 20 + unittests/CppInterOp/Utils.cpp | 13 + unittests/CppInterOp/Utils.h | 10 +- 8 files changed, 1063 insertions(+), 1 deletion(-) create mode 100644 include/clang-c/CXCppInterOp.h create mode 100644 lib/Interpreter/CXCppInterOp.cpp diff --git a/include/clang-c/CXCppInterOp.h b/include/clang-c/CXCppInterOp.h new file mode 100644 index 000000000..336271e02 --- /dev/null +++ b/include/clang-c/CXCppInterOp.h @@ -0,0 +1,366 @@ +// NOLINTBEGIN() +#ifndef LLVM_CLANG_C_CXCPPINTEROP_H +#define LLVM_CLANG_C_CXCPPINTEROP_H + +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" +#include "clang-c/ExternC.h" +#include "clang-c/Index.h" +#include "clang-c/Platform.h" + +#include +#include +#include + +LLVM_CLANG_C_EXTERN_C_BEGIN + +/** + * \defgroup CPPINTEROP_INTERPRETER_MANIP Interpreter manipulations + * + * @{ + */ + +/** + * An opaque pointer representing an interpreter context. + */ +typedef struct CXInterpreterImpl* CXInterpreter; + +/** + * Create a Clang interpreter instance from the given arguments. + * + * \param argv The arguments that would be passed to the interpreter. + * + * \param argc The number of arguments in \c argv. + * + * \returns a \c CXInterpreter. + */ +CXInterpreter clang_createInterpreter(const char* const* argv, int argc); + +typedef void* TInterp_t; + +/** + * Bridge between C API and C++ API. + * + * \returns a \c CXInterpreter. + */ +CXInterpreter clang_createInterpreterFromRawPtr(TInterp_t I); + +/** + * Returns a pointer to the underlying interpreter. + */ +void* clang_Interpreter_getClangInterpreter(CXInterpreter I); + +/** + * Returns a \c TInterp_t and takes the ownership. + */ +TInterp_t clang_Interpreter_takeInterpreterAsPtr(CXInterpreter I); + +/** + * Undo N previous incremental inputs. + */ +enum CXErrorCode clang_Interpreter_Undo(CXInterpreter I, unsigned int N); + +/** + * Dispose of the given interpreter context. + */ +void clang_Interpreter_dispose(CXInterpreter I); + +/** + * Describes the return result of the different routines that do the incremental + * compilation. + */ +typedef enum { + /** + * The compilation was successful. + */ + CXInterpreter_Success = 0, + /** + * The compilation failed. + */ + CXInterpreter_Failure = 1, + /** + * More more input is expected. + */ + CXInterpreter_MoreInputExpected = 2, +} CXInterpreter_CompilationResult; + +/** + * Add a search path to the interpreter. + * + * \param I The interpreter. + * + * \param dir The directory to add. + * + * \param isUser Whether the directory is a user directory. + * + * \param prepend Whether to prepend the directory to the search path. + */ +void clang_Interpreter_addSearchPath(CXInterpreter I, const char* dir, + bool isUser, bool prepend); + +/** + * Add an include path. + * + * \param I The interpreter. + * + * \param dir The directory to add. + */ +void clang_Interpreter_addIncludePath(CXInterpreter I, const char* dir); + +/** + * Declares a code snippet in \c code and does not execute it. + * + * \param I The interpreter. + * + * \param code The code snippet to declare. + * + * \param silent Whether to suppress the diagnostics or not + * + * \returns a \c CXErrorCode. + */ +enum CXErrorCode clang_Interpreter_declare(CXInterpreter I, const char* code, + bool silent); + +/** + * Declares and executes a code snippet in \c code. + * + * \param I The interpreter. + * + * \param code The code snippet to execute. + * + * \returns a \c CXErrorCode. + */ +enum CXErrorCode clang_Interpreter_process(CXInterpreter I, const char* code); + +/** + * An opaque pointer representing a lightweight struct that is used for carrying + * execution results. + */ +typedef void* CXValue; + +/** + * Create a CXValue. + * + * \returns a \c CXValue. + */ +CXValue clang_createValue(void); + +/** + * Dispose of the given CXValue. + * + * \param V The CXValue to dispose. + */ +void clang_Value_dispose(CXValue V); + +/** + * Declares, executes and stores the execution result to \c V. + * + * \param[in] I The interpreter. + * + * \param[in] code The code snippet to evaluate. + * + * \param[out] V The value to store the execution result. + * + * \returns a \c CXErrorCode. + */ +enum CXErrorCode clang_Interpreter_evaluate(CXInterpreter I, const char* code, + CXValue V); + +/** + * Looks up the library if access is enabled. + * + * \param I The interpreter. + * + * \param lib_name The name of the library to lookup. + * + * \returns the path to the library. + */ +CXString clang_Interpreter_lookupLibrary(CXInterpreter I, const char* lib_name); + +/** + * Finds \c lib_stem considering the list of search paths and loads it by + * calling dlopen. + * + * \param I The interpreter. + * + * \param lib_stem The stem of the library to load. + * + * \param lookup Whether to lookup the library or not. + * + * \returns a \c CXInterpreter_CompilationResult. + */ +CXInterpreter_CompilationResult +clang_Interpreter_loadLibrary(CXInterpreter I, const char* lib_stem, + bool lookup); + +/** + * Finds \c lib_stem considering the list of search paths and unloads it by + * calling dlclose. + * + * \param I The interpreter. + * + * \param lib_stem The stem of the library to unload. + */ +void clang_Interpreter_unloadLibrary(CXInterpreter I, const char* lib_stem); + +/** + * @} + */ + +/** + * \defgroup CPPINTEROP_SCOPE_MANIP Scope manipulations + * + * @{ + */ + +/** + * A fake CXCursor for working with the interpreter. + * It has the same structure as CXCursor, but unlike CXCursor, it stores a + * handle to the interpreter in the third slot of the data field. + * This pave the way for upstreaming features to the LLVM project. + */ +typedef struct { + enum CXCursorKind kind; + int xdata; + const void* data[3]; +} CXScope; + +// for debugging purposes +void clang_scope_dump(CXScope S); + +/** + * Checks if a class has a default constructor. + */ +bool clang_hasDefaultConstructor(CXScope S); + +/** + * Returns the default constructor of a class, if any. + */ +CXScope clang_getDefaultConstructor(CXScope S); + +/** + * Returns the class destructor, if any. + */ +CXScope clang_getDestructor(CXScope S); + +/** + * Returns a stringified version of a given function signature in the form: + * void N::f(int i, double d, long l = 0, char ch = 'a'). + */ +CXString clang_getFunctionSignature(CXScope func); + +/** + * Checks if a function is a templated function. + */ +bool clang_isTemplatedFunction(CXScope func); + +/** + * This function performs a lookup to check if there is a templated function of + * that type. \c parent is mandatory, the global scope should be used as the + * default value. + */ +bool clang_existsFunctionTemplate(const char* name, CXScope parent); + +typedef struct { + void* Type; + const char* IntegralValue; +} CXTemplateArgInfo; + +/** + * Builds a template instantiation for a given templated declaration. + * Offers a single interface for instantiation of class, function and variable + * templates. + * + * \param[in] tmpl The uninstantiated template class/function. + * + * \param[in] template_args The pointer to vector of template arguments stored + * in the \c TemplateArgInfo struct + * + * \param[in] template_args_size The size of the vector of template arguments + * passed as \c template_args + * + * \returns a \c CXScope representing the instantiated templated + * class/function/variable. + */ +CXScope clang_instantiateTemplate(CXScope tmpl, + CXTemplateArgInfo* template_args, + size_t template_args_size); + +/** + * A fake CXType for working with the interpreter. + * It has the same structure as CXType, but unlike CXType, it stores a + * handle to the interpreter in the second slot of the data field. + */ +typedef struct { + enum CXTypeKind kind; + void* data[2]; +} CXQualType; + +/** + * Gets the string of the type that is passed as a parameter. + */ +CXString clang_getTypeAsString(CXQualType type); + +/** + * Returns the complex of the provided type. + */ +CXQualType clang_getComplexType(CXQualType eltype); + +/** + * An opaque pointer representing the object of a given type (\c CXScope). + */ +typedef void* CXObject; + +/** + * Allocates memory for the given type. + */ +CXObject clang_allocate(unsigned int n); + +/** + * Deallocates memory for a given class. + */ +void clang_deallocate(CXObject address); + +/** + * Creates an object of class \c scope and calls its default constructor. If \c + * arena is set it uses placement new. + */ +CXObject clang_construct(CXScope scope, void* arena); + +/** + * Creates a trampoline function and makes a call to a generic function or + * method. + * + * \param func The function or method to call. + * + * \param result The location where the return result will be placed. + * + * \param args The arguments to pass to the invocation. + * + * \param n The number of arguments. + * + * \param self The 'this pointer' of the object. + */ +void clang_invoke(CXScope func, void* result, void** args, size_t n, + void* self); + +/** + * Calls the destructor of object of type \c type. When withFree is true it + * calls operator delete/free. + * + * \param This The object to destruct. + * + * \param type The type of the object. + * + * \param withFree Whether to call operator delete/free or not. + */ +void clang_destruct(CXObject This, CXScope S, bool withFree); + +/** + * @} + */ + +LLVM_CLANG_C_EXTERN_C_END + +#endif // LLVM_CLANG_C_CXCPPINTEROP_H + // NOLINTEND() \ No newline at end of file diff --git a/lib/Interpreter/CMakeLists.txt b/lib/Interpreter/CMakeLists.txt index 7f4ca53e1..42e878fe2 100644 --- a/lib/Interpreter/CMakeLists.txt +++ b/lib/Interpreter/CMakeLists.txt @@ -97,6 +97,7 @@ endif(LLVM_LINK_LLVM_DYLIB) add_llvm_library(clangCppInterOp DISABLE_LLVM_LINK_LLVM_DYLIB CppInterOp.cpp + CXCppInterOp.cpp ${DLM} LINK_LIBS ${link_libs} diff --git a/lib/Interpreter/CXCppInterOp.cpp b/lib/Interpreter/CXCppInterOp.cpp new file mode 100644 index 000000000..37c1a149d --- /dev/null +++ b/lib/Interpreter/CXCppInterOp.cpp @@ -0,0 +1,627 @@ +#include "clang-c/CXCppInterOp.h" +#include "Compatibility.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Type.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Interpreter/CppInterOp.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/Support/Casting.h" +#include +#include +#include "clang-c/CXString.h" + +// copied and tweaked from libclang +namespace clang { + +CXCursorKind cxcursor_getCursorKindForDecl(const Decl* D) { + if (!D) + return CXCursor_UnexposedDecl; + + switch (D->getKind()) { + case Decl::Enum: + return CXCursor_EnumDecl; + case Decl::EnumConstant: + return CXCursor_EnumConstantDecl; + case Decl::Field: + return CXCursor_FieldDecl; + case Decl::Function: + return CXCursor_FunctionDecl; + case Decl::CXXMethod: + return CXCursor_CXXMethod; + case Decl::CXXConstructor: + return CXCursor_Constructor; + case Decl::CXXDestructor: + return CXCursor_Destructor; + case Decl::CXXConversion: + return CXCursor_ConversionFunction; + case Decl::ParmVar: + return CXCursor_ParmDecl; + case Decl::Typedef: + return CXCursor_TypedefDecl; + case Decl::TypeAlias: + return CXCursor_TypeAliasDecl; + case Decl::TypeAliasTemplate: + return CXCursor_TypeAliasTemplateDecl; + case Decl::Var: + return CXCursor_VarDecl; + case Decl::Namespace: + return CXCursor_Namespace; + case Decl::NamespaceAlias: + return CXCursor_NamespaceAlias; + case Decl::TemplateTypeParm: + return CXCursor_TemplateTypeParameter; + case Decl::NonTypeTemplateParm: + return CXCursor_NonTypeTemplateParameter; + case Decl::TemplateTemplateParm: + return CXCursor_TemplateTemplateParameter; + case Decl::FunctionTemplate: + return CXCursor_FunctionTemplate; + case Decl::ClassTemplate: + return CXCursor_ClassTemplate; + case Decl::AccessSpec: + return CXCursor_CXXAccessSpecifier; + case Decl::ClassTemplatePartialSpecialization: + return CXCursor_ClassTemplatePartialSpecialization; + case Decl::UsingDirective: + return CXCursor_UsingDirective; + case Decl::StaticAssert: + return CXCursor_StaticAssert; + case Decl::Friend: + return CXCursor_FriendDecl; + case Decl::TranslationUnit: + return CXCursor_TranslationUnit; + + case Decl::Using: + case Decl::UnresolvedUsingValue: + case Decl::UnresolvedUsingTypename: + return CXCursor_UsingDeclaration; + + case Decl::UsingEnum: + return CXCursor_EnumDecl; + + default: + if (const auto* TD = dyn_cast(D)) { + switch (TD->getTagKind()) { +#if CLANG_VERSION_MAJOR >= 18 + case TagTypeKind::Interface: // fall through + case TagTypeKind::Struct: + return CXCursor_StructDecl; + case TagTypeKind::Class: + return CXCursor_ClassDecl; + case TagTypeKind::Union: + return CXCursor_UnionDecl; + case TagTypeKind::Enum: + return CXCursor_EnumDecl; +#else + case TagTypeKind::TTK_Interface: // fall through + case TagTypeKind::TTK_Struct: + return CXCursor_StructDecl; + case TagTypeKind::TTK_Class: + return CXCursor_ClassDecl; + case TagTypeKind::TTK_Union: + return CXCursor_UnionDecl; + case TagTypeKind::TTK_Enum: + return CXCursor_EnumDecl; +#endif + } + } + } + + return CXCursor_UnexposedDecl; +} + +CXTypeKind cxtype_GetBuiltinTypeKind(const BuiltinType* BT) { +#define BTCASE(K) \ + case BuiltinType::K: \ + return CXType_##K + switch (BT->getKind()) { + BTCASE(Void); + BTCASE(Bool); + BTCASE(Char_U); + BTCASE(UChar); + BTCASE(Char16); + BTCASE(Char32); + BTCASE(UShort); + BTCASE(UInt); + BTCASE(ULong); + BTCASE(ULongLong); + BTCASE(UInt128); + BTCASE(Char_S); + BTCASE(SChar); + case BuiltinType::WChar_S: + return CXType_WChar; + case BuiltinType::WChar_U: + return CXType_WChar; + BTCASE(Short); + BTCASE(Int); + BTCASE(Long); + BTCASE(LongLong); + BTCASE(Int128); + BTCASE(Half); + BTCASE(Float); + BTCASE(Double); + BTCASE(LongDouble); + BTCASE(ShortAccum); + BTCASE(Accum); + BTCASE(LongAccum); + BTCASE(UShortAccum); + BTCASE(UAccum); + BTCASE(ULongAccum); + BTCASE(Float16); + BTCASE(Float128); + BTCASE(NullPtr); + default: + return CXType_Unexposed; + } +#undef BTCASE +} + +CXTypeKind cxtype_GetTypeKind(QualType T) { + const Type* TP = T.getTypePtrOrNull(); + if (!TP) + return CXType_Invalid; + +#define TKCASE(K) \ + case Type::K: \ + return CXType_##K + switch (TP->getTypeClass()) { + case Type::Builtin: + return cxtype_GetBuiltinTypeKind(cast(TP)); + TKCASE(Complex); + TKCASE(Pointer); + TKCASE(BlockPointer); + TKCASE(LValueReference); + TKCASE(RValueReference); + TKCASE(Record); + TKCASE(Enum); + TKCASE(Typedef); + TKCASE(ObjCInterface); + TKCASE(ObjCObject); + TKCASE(ObjCObjectPointer); + TKCASE(ObjCTypeParam); + TKCASE(FunctionNoProto); + TKCASE(FunctionProto); + TKCASE(ConstantArray); + TKCASE(IncompleteArray); + TKCASE(VariableArray); + TKCASE(DependentSizedArray); + TKCASE(Vector); + TKCASE(ExtVector); + TKCASE(MemberPointer); + TKCASE(Auto); + TKCASE(Elaborated); + TKCASE(Pipe); + TKCASE(Attributed); +#if CLANG_VERSION_MAJOR >= 16 + TKCASE(BTFTagAttributed); +#endif + TKCASE(Atomic); + default: + return CXType_Unexposed; + } +#undef TKCASE +} + +// FIXME: merge with cxcursor and cxtype in the future +namespace cxscope { + +CXScope MakeCXScope(const clang::Decl* D, const CXInterpreterImpl* I, + SourceRange RegionOfInterest = SourceRange(), + bool FirstInDeclGroup = true) { + assert(D && I && "Invalid arguments!"); + + CXCursorKind K = cxcursor_getCursorKindForDecl(D); + + CXScope S = {K, 0, {D, (void*)(intptr_t)(FirstInDeclGroup ? 1 : 0), I}}; + return S; +} + +CXQualType MakeCXQualType(const clang::QualType Ty, CXInterpreterImpl* I) { + CXTypeKind TK = CXType_Invalid; + TK = cxtype_GetTypeKind(Ty); + + CXQualType CT = {TK, + {TK == CXType_Invalid ? nullptr : Ty.getAsOpaquePtr(), + static_cast(I)}}; + return CT; +} + +} // namespace cxscope + +} // namespace clang + +CXString makeCXString(const std::string& S) { + CXString Str; + if (S.empty()) { + Str.data = ""; + Str.private_flags = 0; // CXS_Unmanaged + } else { + Str.data = strdup(S.c_str()); + Str.private_flags = 1; // CXS_Malloc + } + return Str; +} + +CXStringSet* makeCXStringSet(const std::vector& Strs) { + auto* Set = new CXStringSet; // NOLINT(*-owning-memory) + Set->Count = Strs.size(); + Set->Strings = new CXString[Set->Count]; // NOLINT(*-owning-memory) + for (auto En : llvm::enumerate(Strs)) { + Set->Strings[En.index()] = makeCXString(En.value()); + } + return Set; +} + +struct CXInterpreterImpl { + std::unique_ptr Interp; + // FIXME: find a way to merge this with libclang's CXTranslationUnit + // std::unique_ptr TU; +}; + +static inline compat::Interpreter* getInterpreter(const CXInterpreterImpl* I) { + assert(I && "Invalid interpreter"); + return I->Interp.get(); +} + +CXInterpreter clang_createInterpreter(const char* const* argv, int argc) { + auto* I = new CXInterpreterImpl(); // NOLINT(*-owning-memory) + I->Interp = std::make_unique(argc, argv); + // create a bridge between CXTranslationUnit and clang::Interpreter + // auto AU = std::make_unique(false); + // AU->FileMgr = I->Interp->getCompilerInstance().getFileManager(); + // AU->SourceMgr = I->Interp->getCompilerInstance().getSourceManager(); + // AU->PP = I->Interp->getCompilerInstance().getPreprocessor(); + // AU->Ctx = &I->Interp->getSema().getASTContext(); + // I->TU.reset(MakeCXTranslationUnit(static_cast(clang_createIndex(0, + // 0)), AU)); + return I; +} + +CXInterpreter clang_createInterpreterFromRawPtr(TInterp_t I) { + auto* II = new CXInterpreterImpl(); // NOLINT(*-owning-memory) + II->Interp.reset(static_cast(I)); // NOLINT(*-cast) + return II; +} + +void* clang_Interpreter_getClangInterpreter(CXInterpreter I) { +#ifdef USE_CLING + return nullptr; +#else + auto* interp = getInterpreter(I); + auto* clInterp = &static_cast(*interp); + return clInterp; +#endif // USE_CLING +} + +TInterp_t clang_Interpreter_takeInterpreterAsPtr(CXInterpreter I) { + return static_cast(I)->Interp.release(); +} + +enum CXErrorCode clang_Interpreter_Undo(CXInterpreter I, unsigned int N) { +#ifdef USE_CLING + return CXError_Failure; +#else + return getInterpreter(I)->Undo(N) ? CXError_Failure : CXError_Success; +#endif // USE_CLING +} + +void clang_Interpreter_dispose(CXInterpreter I) { + delete I; // NOLINT(*-owning-memory) +} + +void clang_Interpreter_addSearchPath(CXInterpreter I, const char* dir, + bool isUser, bool prepend) { + auto* interp = getInterpreter(I); + interp->getDynamicLibraryManager()->addSearchPath(dir, isUser, prepend); +} + +void clang_Interpreter_addIncludePath(CXInterpreter I, const char* dir) { + getInterpreter(I)->AddIncludePath(dir); +} + +enum CXErrorCode clang_Interpreter_declare(CXInterpreter I, const char* code, + bool silent) { + auto* interp = getInterpreter(I); + auto& diag = interp->getSema().getDiagnostics(); + + const bool is_silent_old = diag.getSuppressAllDiagnostics(); + + diag.setSuppressAllDiagnostics(silent); + const auto result = interp->declare(code); + diag.setSuppressAllDiagnostics(is_silent_old); + + if (result) + return CXError_Failure; + + return CXError_Success; +} + +enum CXErrorCode clang_Interpreter_process(CXInterpreter I, const char* code) { + if (getInterpreter(I)->process(code)) + return CXError_Failure; + + return CXError_Success; +} + +CXValue clang_createValue(void) { +#ifdef USE_CLING + auto val = std::make_unique(); +#else + auto val = std::make_unique(); +#endif // USE_CLING + + return val.release(); +} + +void clang_Value_dispose(CXValue V) { +#ifdef USE_CLING + delete static_cast(V); // NOLINT(*-owning-memory) +#else + delete static_cast(V); // NOLINT(*-owning-memory) +#endif // USE_CLING +} + +enum CXErrorCode clang_Interpreter_evaluate(CXInterpreter I, const char* code, + CXValue V) { +#ifdef USE_CLING + auto* val = static_cast(V); +#else + auto* val = static_cast(V); +#endif // USE_CLING + + if (getInterpreter(I)->evaluate(code, *val)) + return CXError_Failure; + + return CXError_Success; +} + +CXString clang_Interpreter_lookupLibrary(CXInterpreter I, + const char* lib_name) { + auto* interp = getInterpreter(I); + return makeCXString( + interp->getDynamicLibraryManager()->lookupLibrary(lib_name)); +} + +CXInterpreter_CompilationResult +clang_Interpreter_loadLibrary(CXInterpreter I, const char* lib_stem, + bool lookup) { + auto* interp = getInterpreter(I); + return static_cast( + interp->loadLibrary(lib_stem, lookup)); +} + +void clang_Interpreter_unloadLibrary(CXInterpreter I, const char* lib_stem) { + auto* interp = getInterpreter(I); + interp->getDynamicLibraryManager()->unloadLibrary(lib_stem); +} + +CXString clang_Interpreter_searchLibrariesForSymbol(CXInterpreter I, + const char* mangled_name, + bool search_system) { + auto* interp = getInterpreter(I); + return makeCXString( + interp->getDynamicLibraryManager()->searchLibrariesForSymbol( + mangled_name, search_system)); +} + +namespace Cpp { +bool InsertOrReplaceJitSymbol(compat::Interpreter& I, + const char* linker_mangled_name, + uint64_t address); +} // namespace Cpp + +bool clang_Interpreter_insertOrReplaceJitSymbol(CXInterpreter I, + const char* linker_mangled_name, + uint64_t address) { + return Cpp::InsertOrReplaceJitSymbol(*getInterpreter(I), linker_mangled_name, + address); +} + +static inline clang::QualType getType(const CXQualType& Ty) { + return clang::QualType::getFromOpaquePtr(Ty.data[0]); +} + +static inline CXInterpreterImpl* getNewTU(const CXQualType& Ty) { + return static_cast(Ty.data[1]); +} + +static inline compat::Interpreter* getInterpreter(const CXQualType& Ty) { + return getInterpreter(static_cast(Ty.data[1])); +} + +CXString clang_getTypeAsString(CXQualType type) { + const clang::QualType QT = getType(type); + const auto& C = getInterpreter(type)->getSema().getASTContext(); + clang::PrintingPolicy Policy = C.getPrintingPolicy(); + Policy.Bool = true; // Print bool instead of _Bool. + Policy.SuppressTagKeyword = true; // Do not print `class std::string`. + return makeCXString(compat::FixTypeName(QT.getAsString(Policy))); +} + +CXQualType clang_getComplexType(CXQualType eltype) { + const auto& C = getInterpreter(eltype)->getSema().getASTContext(); + return clang::cxscope::MakeCXQualType(C.getComplexType(getType(eltype)), + getNewTU(eltype)); +} + +static inline bool isNull(const CXScope& S) { return !S.data[0]; } + +static inline clang::Decl* getDecl(const CXScope& S) { + return const_cast(static_cast(S.data[0])); +} + +static inline const CXInterpreterImpl* getNewTU(const CXScope& S) { + return static_cast(S.data[2]); +} + +static inline CXCursorKind kind(const CXScope& S) { return S.kind; } + +static inline compat::Interpreter* getInterpreter(const CXScope& S) { + return getInterpreter(static_cast(S.data[2])); +} + +void clang_scope_dump(CXScope S) { getDecl(S)->dump(); } + +bool clang_hasDefaultConstructor(CXScope S) { + auto* D = getDecl(S); + + if (const auto* CXXRD = llvm::dyn_cast_or_null(D)) + return CXXRD->hasDefaultConstructor(); + + return false; +} + +CXScope clang_getDefaultConstructor(CXScope S) { + if (!clang_hasDefaultConstructor(S)) + return clang::cxscope::MakeCXScope(nullptr, getNewTU(S)); + + auto* CXXRD = llvm::dyn_cast_or_null(getDecl(S)); + if (!CXXRD) + return clang::cxscope::MakeCXScope(nullptr, getNewTU(S)); + + const auto* Res = + getInterpreter(S)->getSema().LookupDefaultConstructor(CXXRD); + return clang::cxscope::MakeCXScope(Res, getNewTU(S)); +} + +CXScope clang_getDestructor(CXScope S) { + auto* D = getDecl(S); + + if (auto* CXXRD = llvm::dyn_cast_or_null(D)) { + getInterpreter(S)->getSema().ForceDeclarationOfImplicitMembers(CXXRD); + return clang::cxscope::MakeCXScope(CXXRD->getDestructor(), getNewTU(S)); + } + + return clang::cxscope::MakeCXScope(nullptr, getNewTU(S)); +} + +CXString clang_getFunctionSignature(CXScope func) { + if (isNull(func)) + return makeCXString(""); + + auto* D = getDecl(func); + if (const auto* FD = llvm::dyn_cast(D)) { + std::string Signature; + llvm::raw_string_ostream SS(Signature); + const auto& C = getInterpreter(func)->getSema().getASTContext(); + clang::PrintingPolicy Policy = C.getPrintingPolicy(); + // Skip printing the body + Policy.TerseOutput = true; + Policy.FullyQualifiedName = true; + Policy.SuppressDefaultTemplateArgs = false; + FD->print(SS, Policy); + SS.flush(); + return makeCXString(Signature); + } + + return makeCXString(""); +} + +bool clang_isTemplatedFunction(CXScope func) { + auto* D = getDecl(func); + if (llvm::isa_and_nonnull(D)) + return true; + + if (const auto* FD = llvm::dyn_cast_or_null(D)) { + const auto TK = FD->getTemplatedKind(); + return TK == clang::FunctionDecl::TemplatedKind:: + TK_FunctionTemplateSpecialization || + TK == clang::FunctionDecl::TemplatedKind:: + TK_DependentFunctionTemplateSpecialization || + TK == clang::FunctionDecl::TemplatedKind::TK_FunctionTemplate; + } + + return false; +} + +bool clang_existsFunctionTemplate(const char* name, CXScope parent) { + if (kind(parent) == CXCursor_FirstInvalid || !name) + return false; + + const auto* Within = llvm::dyn_cast(getDecl(parent)); + + auto& S = getInterpreter(parent)->getSema(); + auto* ND = Cpp::Cpp_utils::Lookup::Named(&S, name, Within); + + if (!ND) + return false; + + if (intptr_t(ND) != (intptr_t)-1) + return clang_isTemplatedFunction( + clang::cxscope::MakeCXScope(ND, getNewTU(parent))); + + // FIXME: Cycle through the Decls and check if there is a templated + return true; +} + +namespace Cpp { +TCppScope_t InstantiateTemplate(compat::Interpreter& I, TCppScope_t tmpl, + const TemplateArgInfo* template_args, + size_t template_args_size); +} // namespace Cpp + +CXScope clang_instantiateTemplate(CXScope tmpl, + CXTemplateArgInfo* template_args, + size_t template_args_size) { + auto* I = getInterpreter(tmpl); + + llvm::SmallVector Info; + for (size_t i = 0; i < template_args_size; ++i) { + Info.push_back(Cpp::TemplateArgInfo(template_args[i].Type, + template_args[i].IntegralValue)); + } + + auto* D = static_cast(Cpp::InstantiateTemplate( + *I, static_cast(getDecl(tmpl)), Info.data(), template_args_size)); + + return clang::cxscope::MakeCXScope(D, getNewTU(tmpl)); +} + +CXObject clang_allocate(unsigned int n) { return ::operator new(n); } + +void clang_deallocate(CXObject address) { ::operator delete(address); } + +CXObject clang_construct(CXScope scope, void* arena) { + if (!clang_hasDefaultConstructor(scope)) + return nullptr; + + const auto Ctor = clang_getDefaultConstructor(scope); + if (kind(Ctor) == CXCursor_FirstInvalid) + return nullptr; + + auto* I = getInterpreter(scope); + if (const Cpp::JitCall JC = Cpp::MakeFunctionCallable(I, getDecl(Ctor))) { + if (arena) { + JC.Invoke(&arena, {}, (void*)~0); // Tell Invoke to use placement new. + return arena; + } + + void* obj = nullptr; + JC.Invoke(&obj); + return obj; + } + + return nullptr; +} + +void clang_invoke(CXScope func, void* result, void** args, size_t n, + void* self) { + Cpp::MakeFunctionCallable(getInterpreter(func), getDecl(func)) + .Invoke(result, {args, n}, self); +} + +namespace Cpp { +void Destruct(compat::Interpreter& interp, TCppObject_t This, + clang::Decl* Class, bool withFree); +} // namespace Cpp + +void clang_destruct(CXObject This, CXScope S, bool withFree) { + Cpp::Destruct(*getInterpreter(S), This, getDecl(S), withFree); +} \ No newline at end of file diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 9b70b8a45..9a0e564f7 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -3,6 +3,8 @@ #include "clang/Interpreter/CppInterOp.h" #include "clang/Basic/Version.h" +#include "clang-c/CXCppInterOp.h" + #include "llvm/ADT/SmallString.h" #include "llvm/Support/Path.h" @@ -98,6 +100,19 @@ TEST(InterpreterTest, CreateInterpreter) { "#endif"); EXPECT_TRUE(Cpp::GetNamed("cpp17")); EXPECT_FALSE(Cpp::GetNamed("cppUnknown")); + + // C API + const char* args[] = {"-std=c++14"}; + auto CXI = clang_createInterpreter(args, 1); + EXPECT_TRUE(CXI); + clang_Interpreter_dispose(CXI); + + CXI = clang_createInterpreterFromRawPtr(I); + auto CLI = clang_Interpreter_getClangInterpreter(CXI); + EXPECT_TRUE(CLI); + auto I2 = clang_Interpreter_takeInterpreterAsPtr(CXI); + EXPECT_EQ(I, I2); + clang_Interpreter_dispose(CXI); } #ifdef LLVM_BINARY_DIR diff --git a/unittests/CppInterOp/ScopeReflectionTest.cpp b/unittests/CppInterOp/ScopeReflectionTest.cpp index d0da1c2f8..11618b7e8 100644 --- a/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -1,6 +1,7 @@ #include "Utils.h" #include "clang/Interpreter/CppInterOp.h" +#include "clang-c/CXCppInterOp.h" #include "clang/AST/ASTContext.h" #include "clang/Interpreter/CppInterOp.h" @@ -753,6 +754,17 @@ TEST(ScopeReflectionTest, InstantiateNNTPClassTemplate) { std::vector args1 = {{IntTy, "5"}}; EXPECT_TRUE(Cpp::InstantiateTemplate(Decls[0], args1.data(), /*type_size*/ args1.size())); + + // C API + auto I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + CXTemplateArgInfo Args1[] = {{IntTy, "5"}}; + auto C_API_SHIM = [&](auto Decl) { + return clang_instantiateTemplate(make_scope(Decl, I), Args1, 1).data[0]; + }; + EXPECT_NE(C_API_SHIM(Decls[0]), nullptr); + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(ScopeReflectionTest, InstantiateVarTemplate) { diff --git a/unittests/CppInterOp/TypeReflectionTest.cpp b/unittests/CppInterOp/TypeReflectionTest.cpp index 0d286e18f..16ee0b8a4 100644 --- a/unittests/CppInterOp/TypeReflectionTest.cpp +++ b/unittests/CppInterOp/TypeReflectionTest.cpp @@ -349,6 +349,26 @@ TEST(TypeReflectionTest, GetComplexType) { EXPECT_EQ(get_complex_type_as_string("int"), "_Complex int"); EXPECT_EQ(get_complex_type_as_string("float"), "_Complex float"); EXPECT_EQ(get_complex_type_as_string("double"), "_Complex double"); + + // C API + auto I = clang_createInterpreterFromRawPtr(Cpp::GetInterpreter()); + auto C_API_SHIM = [&](const std::string& element_type) { + auto ElementQT = Cpp::GetType(element_type); + CXQualType EQT = {CXType_Unexposed, {ElementQT, I}}; + CXQualType ComplexQT = clang_getComplexType(EQT); + auto Str = clang_getTypeAsString(ComplexQT); + auto Res = std::string(clang_getCString(Str)); + clang_disposeString(Str); + return Res; + }; + + EXPECT_EQ(C_API_SHIM("int"), "_Complex int"); + EXPECT_EQ(C_API_SHIM("float"), "_Complex float"); + EXPECT_EQ(C_API_SHIM("double"), "_Complex double"); + + // Clean up resources + clang_Interpreter_takeInterpreterAsPtr(I); + clang_Interpreter_dispose(I); } TEST(TypeReflectionTest, GetTypeFromScope) { diff --git a/unittests/CppInterOp/Utils.cpp b/unittests/CppInterOp/Utils.cpp index 41389ad30..ba5f2f706 100644 --- a/unittests/CppInterOp/Utils.cpp +++ b/unittests/CppInterOp/Utils.cpp @@ -56,3 +56,16 @@ void TestUtils::GetAllSubDecls(Decl *D, std::vector& SubDecls, SubDecls.push_back(Di); } } + +const char* clang_getCString(CXString string) { + return static_cast(string.data); +} + +void clang_disposeString(CXString string) { + if (string.private_flags == 1 && string.data) + free(const_cast(string.data)); +} + +CXScope make_scope(const clang::Decl* D, const CXInterpreter I) { + return {CXCursor_UnexposedDecl, 0, {D, nullptr, I}}; +} \ No newline at end of file diff --git a/unittests/CppInterOp/Utils.h b/unittests/CppInterOp/Utils.h index 8eff16abf..d48171f1d 100644 --- a/unittests/CppInterOp/Utils.h +++ b/unittests/CppInterOp/Utils.h @@ -3,9 +3,11 @@ #include "../../lib/Interpreter/Compatibility.h" +#include "llvm/Support/Valgrind.h" #include #include -#include "llvm/Support/Valgrind.h" +#include "clang-c/CXCppInterOp.h" +#include "clang-c/CXString.h" using namespace clang; using namespace llvm; @@ -21,4 +23,10 @@ namespace TestUtils { bool filter_implicitGenerated = false); } // end namespace TestUtils +// libclang's string manipulation APIs +const char* clang_getCString(CXString string); +void clang_disposeString(CXString string); + +CXScope make_scope(const clang::Decl* D, const CXInterpreter I); + #endif // CPPINTEROP_UNITTESTS_LIBCPPINTEROP_UTILS_H