From 09c71af3a792e6909a04f4caf2c5cc02b31845d6 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 15 Oct 2025 14:07:22 +0200 Subject: [PATCH 1/2] Add CppInterOp API dispatch mechanism This defines the mechanism which enables dispatching of the CppInterOp API without linking to it, preventing any LLVM or Clang symbols from being leaked into the client application. Can be used to deploy CppInterOp in an environment where dynamic linking is not favourable and the only option is dlopen'ing `libClangCppInterOp.so` with `RTLD_LOCAL` --- include/CppInterOp/CppInterOpDispatch.h | 520 ++++++++++++++++++++++++ lib/CppInterOp/CMakeLists.txt | 1 + lib/CppInterOp/CppInterOpDispatch.cpp | 159 ++++++++ 3 files changed, 680 insertions(+) create mode 100644 include/CppInterOp/CppInterOpDispatch.h create mode 100644 lib/CppInterOp/CppInterOpDispatch.cpp diff --git a/include/CppInterOp/CppInterOpDispatch.h b/include/CppInterOp/CppInterOpDispatch.h new file mode 100644 index 000000000..20ca4c09c --- /dev/null +++ b/include/CppInterOp/CppInterOpDispatch.h @@ -0,0 +1,520 @@ +//--------------------------------------------------------------------*- C++ -*- +// CppInterOp Dispatch Mechanism +// author: Aaron Jomy +//===----------------------------------------------------------------------===// +// +// This defines the mechanism which enables dispatching of the CppInterOp API +// without linking to it, preventing any LLVM or Clang symbols from being leaked +// into the client application. +// +//===----------------------------------------------------------------------===// +#ifndef CPPINTEROP_CPPINTEROPDISPATCH_H +#define CPPINTEROP_CPPINTEROPDISPATCH_H + +#include +#include +#include +#include +#include + +#include + +using __CPP_FUNC = void (*)(); +struct FunctionEntry { + const char* name; + __CPP_FUNC address; +}; + +///\param[in] procname - the name of the FunctionEntry in the symbol lookup +/// table. +/// +///\returns the function address of the requested API, or nullptr if not found +extern "C" CPPINTEROP_API void ( + *CppGetProcAddress(const unsigned char* procname))(void); + +#define DECLARE_CPP_NULL(func_name) CppAPIType::func_name func_name = nullptr; + +#define EXTERN_CPP_FUNC(func_name) extern CppAPIType::func_name func_name; + +#define LOAD_CPP_FUNCTION(func_name) \ + func_name = \ + reinterpret_cast(dlGetProcAddress(#func_name)); + +// macro that allows declaration and loading of all CppInterOp API functions in +// a consistent way. This is used as our dispatched API list, along with the +// name-address pair table +#define FOR_EACH_CPP_FUNCTION(DO) \ + DO(CreateInterpreter) \ + DO(GetInterpreter) \ + DO(Process) \ + DO(GetResourceDir) \ + DO(AddIncludePath) \ + DO(LoadLibrary) \ + DO(Declare) \ + DO(DeleteInterpreter) \ + DO(IsNamespace) \ + DO(ObjToString) \ + DO(GetQualifiedCompleteName) \ + DO(IsLValueReferenceType) \ + DO(GetNonReferenceType) \ + DO(IsEnumType) \ + DO(GetIntegerTypeFromEnumType) \ + DO(GetReferencedType) \ + DO(IsPointerType) \ + DO(GetPointeeType) \ + DO(GetPointerType) \ + DO(IsReferenceType) \ + DO(GetTypeAsString) \ + DO(GetCanonicalType) \ + DO(HasTypeQualifier) \ + DO(RemoveTypeQualifier) \ + DO(GetUnderlyingType) \ + DO(IsRecordType) \ + DO(IsFunctionPointerType) \ + DO(GetVariableType) \ + DO(GetNamed) \ + DO(GetScopeFromType) \ + DO(GetClassTemplateInstantiationArgs) \ + DO(IsClass) \ + DO(GetType) \ + DO(GetTypeFromScope) \ + DO(GetComplexType) \ + DO(GetIntegerTypeFromEnumScope) \ + DO(GetUnderlyingScope) \ + DO(GetScope) \ + DO(GetGlobalScope) \ + DO(GetScopeFromCompleteName) \ + DO(InstantiateTemplate) \ + DO(GetParentScope) \ + DO(IsTemplate) \ + DO(IsTemplateSpecialization) \ + DO(IsTypedefed) \ + DO(IsClassPolymorphic) \ + DO(Demangle) \ + DO(SizeOf) \ + DO(GetSizeOfType) \ + DO(IsBuiltin) \ + DO(IsComplete) \ + DO(Allocate) \ + DO(Deallocate) \ + DO(Construct) \ + DO(Destruct) \ + DO(MakeFunctionCallable) \ + DO(GetFunctionAddress) \ + DO(IsAbstract) \ + DO(IsEnumScope) \ + DO(IsEnumConstant) \ + DO(IsAggregate) \ + DO(HasDefaultConstructor) \ + DO(IsVariable) \ + DO(GetAllCppNames) \ + DO(GetUsingNamespaces) \ + DO(GetCompleteName) \ + DO(GetDestructor) \ + DO(IsVirtualMethod) \ + DO(GetNumBases) \ + DO(GetName) \ + DO(GetBaseClass) \ + DO(IsSubclass) \ + DO(GetOperator) \ + DO(GetFunctionReturnType) \ + DO(GetBaseClassOffset) \ + DO(GetClassMethods) \ + DO(GetFunctionsUsingName) \ + DO(GetFunctionNumArgs) \ + DO(GetFunctionRequiredArgs) \ + DO(GetFunctionArgName) \ + DO(GetFunctionArgType) \ + DO(GetFunctionArgDefault) \ + DO(IsConstMethod) \ + DO(GetFunctionTemplatedDecls) \ + DO(ExistsFunctionTemplate) \ + DO(IsTemplatedFunction) \ + DO(IsStaticMethod) \ + DO(GetClassTemplatedMethods) \ + DO(BestOverloadFunctionMatch) \ + DO(GetOperatorFromSpelling) \ + DO(IsFunctionDeleted) \ + DO(IsPublicMethod) \ + DO(IsProtectedMethod) \ + DO(IsPrivateMethod) \ + DO(IsConstructor) \ + DO(IsDestructor) \ + DO(GetDatamembers) \ + DO(GetStaticDatamembers) \ + DO(GetEnumConstantDatamembers) \ + DO(LookupDatamember) \ + DO(IsLambdaClass) \ + DO(GetQualifiedName) \ + DO(GetVariableOffset) \ + DO(IsPublicVariable) \ + DO(IsProtectedVariable) \ + DO(IsPrivateVariable) \ + DO(IsStaticVariable) \ + DO(IsConstVariable) \ + DO(GetDimensions) \ + DO(GetEnumConstants) \ + DO(GetEnumConstantType) \ + DO(GetEnumConstantValue) \ + DO(DumpScope) \ + DO(AddSearchPath) \ + DO(Evaluate) \ + DO(IsDebugOutputEnabled) \ + DO(EnableDebugOutput) + +namespace CppDispatch { +// Forward all type aliases +using TCppIndex_t = ::Cpp::TCppIndex_t; +using TCppScope_t = ::Cpp::TCppScope_t; +using TCppConstScope_t = ::Cpp::TCppConstScope_t; +using TCppType_t = ::Cpp::TCppType_t; +using TCppFunction_t = ::Cpp::TCppFunction_t; +using TCppConstFunction_t = ::Cpp::TCppConstFunction_t; +using TCppFuncAddr_t = ::Cpp::TCppFuncAddr_t; +using TInterp_t = ::Cpp::TInterp_t; +using TCppObject_t = ::Cpp::TCppObject_t; + +using Operator = ::Cpp::Operator; +using OperatorArity = ::Cpp::OperatorArity; +using QualKind = ::Cpp::QualKind; +using TemplateArgInfo = ::Cpp::TemplateArgInfo; + +using JitCall = ::Cpp::JitCall; +} // end namespace CppDispatch + +namespace CppAPIType { + +using GetVersion = std::string (*)(); + +using Demangle = std::string (*)(const std::string& mangled_name); + +using EnableDebugOutput = void (*)(bool value); + +using IsDebugOutputEnabled = bool (*)(); + +using IsAggregate = bool (*)(Cpp::TCppScope_t scope); + +using IsNamespace = bool (*)(Cpp::TCppScope_t scope); + +using IsClass = bool (*)(Cpp::TCppScope_t scope); + +using IsFunction = bool (*)(Cpp::TCppScope_t scope); + +using IsFunctionPointerType = bool (*)(Cpp::TCppType_t type); + +using IsClassPolymorphic = bool (*)(Cpp::TCppScope_t klass); + +using IsComplete = bool (*)(Cpp::TCppScope_t scope); + +using SizeOf = size_t (*)(Cpp::TCppScope_t scope); + +using IsBuiltin = bool (*)(Cpp::TCppType_t type); + +using IsTemplate = bool (*)(Cpp::TCppScope_t handle); + +using IsTemplateSpecialization = bool (*)(Cpp::TCppScope_t handle); + +using IsTypedefed = bool (*)(Cpp::TCppScope_t handle); + +using IsAbstract = bool (*)(Cpp::TCppType_t klass); + +using IsEnumScope = bool (*)(Cpp::TCppScope_t handle); + +using IsEnumConstant = bool (*)(Cpp::TCppScope_t handle); + +using IsEnumType = bool (*)(Cpp::TCppType_t type); + +using HasTypeQualifier = bool (*)(Cpp::TCppType_t type, Cpp::QualKind qual); + +using RemoveTypeQualifier = Cpp::TCppType_t (*)(Cpp::TCppType_t type, + Cpp::QualKind qual); + +using AddTypeQualifier = Cpp::TCppType_t (*)(Cpp::TCppType_t type, + Cpp::QualKind qual); + +using GetEnums = void (*)(Cpp::TCppScope_t scope, + std::vector& Result); + +using IsSmartPtrType = bool (*)(Cpp::TCppType_t type); + +using GetIntegerTypeFromEnumScope = + Cpp::TCppType_t (*)(Cpp::TCppScope_t handle); + +using GetIntegerTypeFromEnumType = Cpp::TCppType_t (*)(Cpp::TCppType_t handle); + +using GetEnumConstants = + std::vector (*)(Cpp::TCppScope_t scope); + +using GetEnumConstantType = Cpp::TCppType_t (*)(Cpp::TCppScope_t scope); + +using GetEnumConstantValue = Cpp::TCppIndex_t (*)(Cpp::TCppScope_t scope); + +using GetSizeOfType = size_t (*)(Cpp::TCppType_t type); + +using IsVariable = bool (*)(Cpp::TCppScope_t scope); + +using GetName = std::string (*)(Cpp::TCppScope_t klass); + +using GetCompleteName = std::string (*)(Cpp::TCppScope_t klass); + +using GetQualifiedName = std::string (*)(Cpp::TCppScope_t klass); + +using GetQualifiedCompleteName = std::string (*)(Cpp::TCppScope_t klass); + +using GetUsingNamespaces = + std::vector (*)(Cpp::TCppScope_t scope); + +using GetGlobalScope = Cpp::TCppScope_t (*)(); + +using GetUnderlyingScope = Cpp::TCppScope_t (*)(Cpp::TCppScope_t scope); + +using GetScope = Cpp::TCppScope_t (*)(const std::string& name, + Cpp::TCppScope_t parent); + +using GetScopeFromCompleteName = Cpp::TCppScope_t (*)(const std::string& name); + +using GetNamed = Cpp::TCppScope_t (*)(const std::string& name, + Cpp::TCppScope_t parent); + +using GetParentScope = Cpp::TCppScope_t (*)(Cpp::TCppScope_t scope); + +using GetScopeFromType = Cpp::TCppScope_t (*)(Cpp::TCppType_t type); + +using GetNumBases = Cpp::TCppIndex_t (*)(Cpp::TCppScope_t klass); + +using GetBaseClass = Cpp::TCppScope_t (*)(Cpp::TCppScope_t klass, + Cpp::TCppIndex_t ibase); + +using IsSubclass = bool (*)(Cpp::TCppScope_t derived, Cpp::TCppScope_t base); + +using GetBaseClassOffset = int64_t (*)(Cpp::TCppScope_t derived, + Cpp::TCppScope_t base); + +using GetClassMethods = void (*)(Cpp::TCppScope_t klass, + std::vector& methods); + +using GetFunctionTemplatedDecls = + void (*)(Cpp::TCppScope_t klass, std::vector& methods); + +using HasDefaultConstructor = bool (*)(Cpp::TCppScope_t scope); + +using GetDefaultConstructor = Cpp::TCppFunction_t (*)(Cpp::TCppScope_t scope); + +using GetDestructor = Cpp::TCppFunction_t (*)(Cpp::TCppScope_t scope); + +using GetFunctionsUsingName = std::vector (*)( + Cpp::TCppScope_t scope, const std::string& name); + +using GetFunctionReturnType = Cpp::TCppType_t (*)(Cpp::TCppFunction_t func); + +using GetFunctionNumArgs = Cpp::TCppIndex_t (*)(Cpp::TCppFunction_t func); + +using GetFunctionRequiredArgs = + Cpp::TCppIndex_t (*)(Cpp::TCppConstFunction_t func); + +using GetFunctionArgType = Cpp::TCppType_t (*)(Cpp::TCppFunction_t func, + Cpp::TCppIndex_t iarg); + +using GetFunctionSignature = std::string (*)(Cpp::TCppFunction_t func); + +using IsFunctionDeleted = bool (*)(Cpp::TCppConstFunction_t function); + +using IsTemplatedFunction = bool (*)(Cpp::TCppFunction_t func); + +using ExistsFunctionTemplate = bool (*)(const std::string& name, + Cpp::TCppScope_t parent); + +using GetClassTemplatedMethods = + bool (*)(const std::string& name, Cpp::TCppScope_t parent, + std::vector& funcs); + +using IsMethod = bool (*)(Cpp::TCppConstFunction_t method); + +using IsPublicMethod = bool (*)(Cpp::TCppFunction_t method); + +using IsProtectedMethod = bool (*)(Cpp::TCppFunction_t method); + +using IsPrivateMethod = bool (*)(Cpp::TCppFunction_t method); + +using IsConstructor = bool (*)(Cpp::TCppConstFunction_t method); + +using IsDestructor = bool (*)(Cpp::TCppConstFunction_t method); + +using IsStaticMethod = bool (*)(Cpp::TCppConstFunction_t method); + +using GetFunctionAddressFromName = + Cpp::TCppFuncAddr_t (*)(const char* mangled_name); + +using GetFunctionAddressFromMethod = + Cpp::TCppFuncAddr_t (*)(Cpp::TCppFunction_t method); + +using IsVirtualMethod = bool (*)(Cpp::TCppFunction_t method); + +using GetDatamembers = void (*)(Cpp::TCppScope_t scope, + std::vector& datamembers); + +using GetStaticDatamembers = void (*)( + Cpp::TCppScope_t scope, std::vector& datamembers); + +using GetEnumConstantDatamembers = + void (*)(Cpp::TCppScope_t scope, std::vector& datamembers, + bool include_enum_class); + +using LookupDatamember = Cpp::TCppScope_t (*)(const std::string& name, + Cpp::TCppScope_t parent); + +using IsLambdaClass = bool (*)(Cpp::TCppType_t type); + +using GetVariableType = Cpp::TCppType_t (*)(Cpp::TCppScope_t var); + +using GetVariableOffset = intptr_t (*)(Cpp::TCppScope_t var, + Cpp::TCppScope_t parent); + +using IsPublicVariable = bool (*)(Cpp::TCppScope_t var); + +using IsProtectedVariable = bool (*)(Cpp::TCppScope_t var); + +using IsPrivateVariable = bool (*)(Cpp::TCppScope_t var); + +using IsStaticVariable = bool (*)(Cpp::TCppScope_t var); + +using IsConstVariable = bool (*)(Cpp::TCppScope_t var); + +using IsRecordType = bool (*)(Cpp::TCppType_t type); + +using IsPODType = bool (*)(Cpp::TCppType_t type); + +using IsPointerType = bool (*)(Cpp::TCppType_t type); + +using GetPointeeType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using IsReferenceType = bool (*)(Cpp::TCppType_t type); + +using IsLValueReferenceType = bool (*)(Cpp::TCppType_t type); + +using IsRValueReferenceType = bool (*)(Cpp::TCppType_t type); + +using GetPointerType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using GetReferencedType = Cpp::TCppType_t (*)(Cpp::TCppType_t type, + bool rvalue); + +using GetNonReferenceType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using GetTypeAsString = std::string (*)(Cpp::TCppType_t type); + +using GetCanonicalType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using JitCallMakeFunctionCallable = + Cpp::JitCall (*)(Cpp::TCppConstFunction_t func); + +using IsConstMethod = bool (*)(Cpp::TCppFunction_t method); + +using GetFunctionArgDefault = std::string (*)(Cpp::TCppFunction_t func, + Cpp::TCppIndex_t param_index); + +using GetFunctionArgName = std::string (*)(Cpp::TCppFunction_t func, + Cpp::TCppIndex_t param_index); + +using GetSpellingFromOperator = std::string (*)(Cpp::Operator op); + +using GetOperatorFromSpelling = Cpp::Operator (*)(const std::string& op); + +using GetOperatorArity = Cpp::OperatorArity (*)(Cpp::TCppFunction_t op); + +using GetOperator = void (*)(Cpp::TCppScope_t scope, Cpp::Operator op, + std::vector& operators, + Cpp::OperatorArity kind); + +using CreateInterpreter = + Cpp::TInterp_t (*)(const std::vector& Args, + const std::vector& GpuArgs); + +using DeleteInterpreter = bool (*)(Cpp::TInterp_t interp); + +using ActivateInterpreter = bool (*)(Cpp::TInterp_t interp); + +using GetInterpreter = Cpp::TInterp_t (*)(); + +using UseExternalInterpreter = void (*)(Cpp::TInterp_t interp); + +using AddSearchPath = void (*)(const char* dir, bool isUser, bool prepend); + +using GetResourceDir = const char* (*)(); + +using DetectResourceDir = std::string (*)(const char* ClangBinaryName); + +using DetectSystemCompilerIncludePaths = + void (*)(std::vector& Paths, const char* CompilerName); + +using AddIncludePath = void (*)(const char* dir); + +using GetIncludePaths = void (*)(std::vector& IncludePaths, + bool withSystem, bool withFlags); + +using Declare = int (*)(const char* code, bool silent); + +using Process = int (*)(const char* code); + +using Evaluate = intptr_t (*)(const char* code, bool* HadError); + +using LookupLibrary = std::string (*)(const char* lib_name); + +using LoadLibrary = bool (*)(const char* lib_stem, bool lookup); + +using UnloadLibrary = void (*)(const char* lib_stem); + +using SearchLibrariesForSymbol = std::string (*)(const char* mangled_name, + bool search_system); + +using InsertOrReplaceJitSymbol = bool (*)(const char* linker_mangled_name, + uint64_t address); + +using ObjToString = std::string (*)(const char* type, void* obj); + +using GetUnderlyingType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using BestOverloadFunctionMatch = Cpp::TCppFunction_t (*)( + const std::vector& candidates, + const std::vector& explicit_types, + const std::vector& arg_types); + +using GetDimensions = std::vector (*)(Cpp::TCppType_t var); + +using DumpScope = void (*)(Cpp::TCppScope_t scope); + +using GetClassTemplateInstantiationArgs = + void (*)(Cpp::TCppScope_t klass, std::vector& args); + +using GetAllCppNames = void (*)(Cpp::TCppScope_t scope, + std::set& names); + +using Deallocate = void (*)(Cpp::TCppScope_t scope, Cpp::TCppObject_t address, + Cpp::TCppIndex_t count); + +using Allocate = Cpp::TCppObject_t (*)(Cpp::TCppScope_t scope, + Cpp::TCppIndex_t count); + +using InstantiateTemplate = Cpp::TCppScope_t (*)( + Cpp::TCppScope_t tmpl, const Cpp::TemplateArgInfo* template_args, + size_t template_args_size, bool instantiate_body); + +using GetComplexType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using GetTypeFromScope = Cpp::TCppType_t (*)(Cpp::TCppScope_t klass); + +using GetType = Cpp::TCppType_t (*)(const std::string& type); + +using Construct = Cpp::TCppObject_t (*)(Cpp::TCppScope_t scope, + Cpp::TCppObject_t arena, + Cpp::TCppIndex_t count); + +using Destruct = bool (*)(Cpp::TCppObject_t This, Cpp::TCppScope_t scope, + bool withFree, Cpp::TCppIndex_t count); + +using MakeFunctionCallable = Cpp::JitCall (*)(Cpp::TCppConstFunction_t func); +using GetFunctionAddress = + Cpp::TCppFuncAddr_t (*)(Cpp::TCppConstFunction_t func); +} // end namespace CppAPIType + +#endif // CPPINTEROP_CPPINTEROPDISPATCH_H diff --git a/lib/CppInterOp/CMakeLists.txt b/lib/CppInterOp/CMakeLists.txt index 41288fb60..fec2def58 100644 --- a/lib/CppInterOp/CMakeLists.txt +++ b/lib/CppInterOp/CMakeLists.txt @@ -109,6 +109,7 @@ endif() add_llvm_library(clangCppInterOp DISABLE_LLVM_LINK_LLVM_DYLIB CppInterOp.cpp + CppInterOpDispatch.cpp CXCppInterOp.cpp ${DLM} LINK_LIBS diff --git a/lib/CppInterOp/CppInterOpDispatch.cpp b/lib/CppInterOp/CppInterOpDispatch.cpp new file mode 100644 index 000000000..40f227f16 --- /dev/null +++ b/lib/CppInterOp/CppInterOpDispatch.cpp @@ -0,0 +1,159 @@ +//------------------------------------------------------------------------------ +// CppInterOp Dispatch Implementation +// author: Aaron Jomy +//------------------------------------------------------------------------------ + +#include +#include + +#include + +static const FunctionEntry api_function_table[] = { + {"GetInterpreter", (__CPP_FUNC)Cpp::GetInterpreter}, + {"CreateInterpreter", (__CPP_FUNC)Cpp::CreateInterpreter}, + {"Process", (__CPP_FUNC)Cpp::Process}, + {"GetResourceDir", (__CPP_FUNC)Cpp::GetResourceDir}, + {"AddIncludePath", (__CPP_FUNC)Cpp::AddIncludePath}, + {"LoadLibrary", (__CPP_FUNC)Cpp::LoadLibrary}, + {"Declare", (__CPP_FUNC)Cpp::Declare}, + {"DeleteInterpreter", (__CPP_FUNC)Cpp::DeleteInterpreter}, + {"IsNamespace", (__CPP_FUNC)Cpp::IsNamespace}, + {"ObjToString", (__CPP_FUNC)Cpp::ObjToString}, + {"GetQualifiedCompleteName", (__CPP_FUNC)Cpp::GetQualifiedCompleteName}, + {"IsLValueReferenceType", (__CPP_FUNC)Cpp::IsLValueReferenceType}, + {"GetNonReferenceType", (__CPP_FUNC)Cpp::GetNonReferenceType}, + {"IsEnumType", (__CPP_FUNC)Cpp::IsEnumType}, + {"GetIntegerTypeFromEnumType", (__CPP_FUNC)Cpp::GetIntegerTypeFromEnumType}, + {"GetReferencedType", (__CPP_FUNC)Cpp::GetReferencedType}, + {"IsPointerType", (__CPP_FUNC)Cpp::IsPointerType}, + {"GetPointeeType", (__CPP_FUNC)Cpp::GetPointeeType}, + {"GetPointerType", (__CPP_FUNC)Cpp::GetPointerType}, + {"IsReferenceType", (__CPP_FUNC)Cpp::IsReferenceType}, + {"GetTypeAsString", (__CPP_FUNC)Cpp::GetTypeAsString}, + {"GetCanonicalType", (__CPP_FUNC)Cpp::GetCanonicalType}, + {"HasTypeQualifier", (__CPP_FUNC)Cpp::HasTypeQualifier}, + {"RemoveTypeQualifier", (__CPP_FUNC)Cpp::RemoveTypeQualifier}, + {"GetUnderlyingType", (__CPP_FUNC)Cpp::GetUnderlyingType}, + {"IsRecordType", (__CPP_FUNC)Cpp::IsRecordType}, + {"IsFunctionPointerType", (__CPP_FUNC)Cpp::IsFunctionPointerType}, + {"GetVariableType", (__CPP_FUNC)Cpp::GetVariableType}, + {"GetNamed", (__CPP_FUNC)Cpp::GetNamed}, + {"GetScopeFromType", (__CPP_FUNC)Cpp::GetScopeFromType}, + {"GetClassTemplateInstantiationArgs", + (__CPP_FUNC)Cpp::GetClassTemplateInstantiationArgs}, + {"IsClass", (__CPP_FUNC)Cpp::IsClass}, + {"GetType", (__CPP_FUNC)Cpp::GetType}, + {"GetTypeFromScope", (__CPP_FUNC)Cpp::GetTypeFromScope}, + {"GetComplexType", (__CPP_FUNC)Cpp::GetComplexType}, + {"GetIntegerTypeFromEnumScope", + (__CPP_FUNC)Cpp::GetIntegerTypeFromEnumScope}, + {"GetUnderlyingScope", (__CPP_FUNC)Cpp::GetUnderlyingScope}, + {"GetScope", (__CPP_FUNC)Cpp::GetScope}, + {"GetGlobalScope", (__CPP_FUNC)Cpp::GetGlobalScope}, + {"GetScopeFromCompleteName", (__CPP_FUNC)Cpp::GetScopeFromCompleteName}, + {"InstantiateTemplate", (__CPP_FUNC)Cpp::InstantiateTemplate}, + {"GetParentScope", (__CPP_FUNC)Cpp::GetParentScope}, + {"IsTemplate", (__CPP_FUNC)Cpp::IsTemplate}, + {"IsTemplateSpecialization", (__CPP_FUNC)Cpp::IsTemplateSpecialization}, + {"IsTypedefed", (__CPP_FUNC)Cpp::IsTypedefed}, + {"IsClassPolymorphic", (__CPP_FUNC)Cpp::IsClassPolymorphic}, + {"Demangle", (__CPP_FUNC)Cpp::Demangle}, + {"SizeOf", (__CPP_FUNC)Cpp::SizeOf}, + {"GetSizeOfType", (__CPP_FUNC)Cpp::GetSizeOfType}, + {"IsBuiltin", (__CPP_FUNC)Cpp::IsBuiltin}, + {"IsComplete", (__CPP_FUNC)Cpp::IsComplete}, + {"Allocate", (__CPP_FUNC)Cpp::Allocate}, + {"Deallocate", (__CPP_FUNC)Cpp::Deallocate}, + {"Construct", (__CPP_FUNC)Cpp::Construct}, + {"Destruct", (__CPP_FUNC)Cpp::Destruct}, + {"MakeFunctionCallable", + (__CPP_FUNC) static_cast( + &Cpp::MakeFunctionCallable)}, + {"GetFunctionAddress", + (__CPP_FUNC) static_cast( + &Cpp::GetFunctionAddress)}, + {"IsAbstract", (__CPP_FUNC)Cpp::IsAbstract}, + {"IsEnumScope", (__CPP_FUNC)Cpp::IsEnumScope}, + {"IsEnumConstant", (__CPP_FUNC)Cpp::IsEnumConstant}, + {"IsAggregate", (__CPP_FUNC)Cpp::IsAggregate}, + {"HasDefaultConstructor", (__CPP_FUNC)Cpp::HasDefaultConstructor}, + {"IsVariable", (__CPP_FUNC)Cpp::IsVariable}, + {"GetAllCppNames", (__CPP_FUNC)Cpp::GetAllCppNames}, + {"GetUsingNamespaces", (__CPP_FUNC)Cpp::GetUsingNamespaces}, + {"GetCompleteName", (__CPP_FUNC)Cpp::GetCompleteName}, + {"GetDestructor", (__CPP_FUNC)Cpp::GetDestructor}, + {"IsVirtualMethod", (__CPP_FUNC)Cpp::IsVirtualMethod}, + {"GetNumBases", (__CPP_FUNC)Cpp::GetNumBases}, + {"GetName", (__CPP_FUNC)Cpp::GetName}, + {"GetBaseClass", (__CPP_FUNC)Cpp::GetBaseClass}, + {"IsSubclass", (__CPP_FUNC)Cpp::IsSubclass}, + {"GetOperator", (__CPP_FUNC)Cpp::GetOperator}, + {"GetFunctionReturnType", (__CPP_FUNC)Cpp::GetFunctionReturnType}, + {"GetBaseClassOffset", (__CPP_FUNC)Cpp::GetBaseClassOffset}, + {"GetClassMethods", (__CPP_FUNC)Cpp::GetClassMethods}, + {"GetFunctionsUsingName", (__CPP_FUNC)Cpp::GetFunctionsUsingName}, + {"GetFunctionNumArgs", (__CPP_FUNC)Cpp::GetFunctionNumArgs}, + {"GetFunctionRequiredArgs", (__CPP_FUNC)Cpp::GetFunctionRequiredArgs}, + {"GetFunctionArgName", (__CPP_FUNC)Cpp::GetFunctionArgName}, + {"GetFunctionArgType", (__CPP_FUNC)Cpp::GetFunctionArgType}, + {"GetFunctionArgDefault", (__CPP_FUNC)Cpp::GetFunctionArgDefault}, + {"IsConstMethod", (__CPP_FUNC)Cpp::IsConstMethod}, + {"GetFunctionTemplatedDecls", (__CPP_FUNC)Cpp::GetFunctionTemplatedDecls}, + {"ExistsFunctionTemplate", (__CPP_FUNC)Cpp::ExistsFunctionTemplate}, + {"IsTemplatedFunction", (__CPP_FUNC)Cpp::IsTemplatedFunction}, + {"IsStaticMethod", (__CPP_FUNC)Cpp::IsStaticMethod}, + {"GetClassTemplatedMethods", (__CPP_FUNC)Cpp::GetClassTemplatedMethods}, + {"BestOverloadFunctionMatch", (__CPP_FUNC)Cpp::BestOverloadFunctionMatch}, + {"GetOperatorFromSpelling", (__CPP_FUNC)Cpp::GetOperatorFromSpelling}, + {"IsFunctionDeleted", (__CPP_FUNC)Cpp::IsFunctionDeleted}, + {"IsPublicMethod", (__CPP_FUNC)Cpp::IsPublicMethod}, + {"IsProtectedMethod", (__CPP_FUNC)Cpp::IsProtectedMethod}, + {"IsPrivateMethod", (__CPP_FUNC)Cpp::IsPrivateMethod}, + {"IsConstructor", (__CPP_FUNC)Cpp::IsConstructor}, + {"IsDestructor", (__CPP_FUNC)Cpp::IsDestructor}, + {"GetDatamembers", (__CPP_FUNC)Cpp::GetDatamembers}, + {"GetStaticDatamembers", (__CPP_FUNC)Cpp::GetStaticDatamembers}, + {"GetEnumConstantDatamembers", (__CPP_FUNC)Cpp::GetEnumConstantDatamembers}, + {"LookupDatamember", (__CPP_FUNC)Cpp::LookupDatamember}, + {"IsLambdaClass", (__CPP_FUNC)Cpp::IsLambdaClass}, + {"GetQualifiedName", (__CPP_FUNC)Cpp::GetQualifiedName}, + {"GetVariableOffset", (__CPP_FUNC)Cpp::GetVariableOffset}, + {"IsPublicVariable", (__CPP_FUNC)Cpp::IsPublicVariable}, + {"IsProtectedVariable", (__CPP_FUNC)Cpp::IsProtectedVariable}, + {"IsPrivateVariable", (__CPP_FUNC)Cpp::IsPrivateVariable}, + {"IsStaticVariable", (__CPP_FUNC)Cpp::IsStaticVariable}, + {"IsConstVariable", (__CPP_FUNC)Cpp::IsConstVariable}, + {"GetDimensions", (__CPP_FUNC)Cpp::GetDimensions}, + {"GetEnumConstants", (__CPP_FUNC)Cpp::GetEnumConstants}, + {"GetEnumConstantType", (__CPP_FUNC)Cpp::GetEnumConstantType}, + {"GetEnumConstantValue", (__CPP_FUNC)Cpp::GetEnumConstantValue}, + {"DumpScope", (__CPP_FUNC)Cpp::DumpScope}, + {"AddSearchPath", (__CPP_FUNC)Cpp::AddSearchPath}, + {"Evaluate", (__CPP_FUNC)Cpp::Evaluate}, + {"IsDebugOutputEnabled", (__CPP_FUNC)Cpp::IsDebugOutputEnabled}, + {"EnableDebugOutput", (__CPP_FUNC)Cpp::EnableDebugOutput}, + {nullptr, nullptr} /* end of list */ +}; + +static inline __CPP_FUNC _cppinterop_get_proc_address(const char* funcName) { + static const std::unordered_map function_map = + [] { + std::unordered_map map; + size_t count = 0; + while (api_function_table[count].name != nullptr) + ++count; + map.reserve(count); + + for (size_t i = 0; i < count; ++i) { + map[api_function_table[i].name] = api_function_table[i].address; + } + return map; + }(); + + auto it = function_map.find(funcName); + return (it != function_map.end()) ? it->second : nullptr; +} + +void (*CppGetProcAddress(const unsigned char* procName))(void) { + return _cppinterop_get_proc_address(reinterpret_cast(procName)); +} From 460f4935bbb93a3fee2c44ada9482aaaea2d22be Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 15 Oct 2025 14:10:14 +0200 Subject: [PATCH 2/2] [tests] Provide unittest/example for API dispatch mechanism to be followed up with a section in docs --- unittests/CppInterOp/CMakeLists.txt | 8 +++ unittests/CppInterOp/DispatchAPITest.cpp | 67 ++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 unittests/CppInterOp/DispatchAPITest.cpp diff --git a/unittests/CppInterOp/CMakeLists.txt b/unittests/CppInterOp/CMakeLists.txt index 4b4b43bdd..1440c972c 100644 --- a/unittests/CppInterOp/CMakeLists.txt +++ b/unittests/CppInterOp/CMakeLists.txt @@ -11,6 +11,14 @@ else() set(EXTRA_PATH_TEST_BINARIES /CppInterOpTests/unittests/bin/$/) endif() +# Add the DispatchAPITest only when building shared libraries +if (BUILD_SHARED_LIBS) + set_source_files_properties(DispatchAPITest.cpp PROPERTIES COMPILE_DEFINITIONS + "CPPINTEROP_LIB_DIR=\"${CMAKE_BINARY_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}\"" + ) + list(APPEND EXTRA_TEST_SOURCE_FILES DispatchAPITest.cpp) +endif() + add_cppinterop_unittest(CppInterOpTests EnumReflectionTest.cpp FunctionReflectionTest.cpp diff --git a/unittests/CppInterOp/DispatchAPITest.cpp b/unittests/CppInterOp/DispatchAPITest.cpp new file mode 100644 index 000000000..303cb45cc --- /dev/null +++ b/unittests/CppInterOp/DispatchAPITest.cpp @@ -0,0 +1,67 @@ +#include "Utils.h" + +#include "CppInterOp/CppInterOpDispatch.h" + +#include "gtest/gtest.h" + +#include + +using namespace TestUtils; +using namespace llvm; +using namespace clang; + +static void* dlGetProcAddress(const char* name) { + static void* handle = dlopen(CPPINTEROP_LIB_DIR, RTLD_LAZY | RTLD_LOCAL); + EXPECT_NE(handle, nullptr) << "Failed to open library: " << dlerror(); + if (!handle) + return nullptr; + static void* gpa = dlsym(handle, "CppGetProcAddress"); + EXPECT_NE(gpa, nullptr) << "Failed to find GetProcAddress: " << dlerror(); + return reinterpret_cast(gpa)(name); +} + +TEST(DispatchAPITestTest, IsClassSymbolLookup) { + CppAPIType::IsClass IsClassFn = + reinterpret_cast(dlGetProcAddress("IsClass")); + ASSERT_NE(IsClassFn, nullptr) << "failed to locate symbol: " << dlerror(); + std::vector Decls; + GetAllTopLevelDecls("namespace N {} class C{}; int I;", Decls); + EXPECT_FALSE(IsClassFn(Decls[0])); + EXPECT_TRUE(IsClassFn(Decls[1])); + EXPECT_FALSE(IsClassFn(Decls[2])); +} + +TEST(DispatchAPITestTest, Demangle) { + + std::string code = R"( + int add(int x, int y) { return x + y; } + int add(double x, double y) { return x + y; } + )"; + + std::vector Decls; + GetAllTopLevelDecls(code, Decls); + EXPECT_EQ(Decls.size(), 2); + + auto Add_int = clang::GlobalDecl(static_cast(Decls[0])); + auto Add_double = clang::GlobalDecl(static_cast(Decls[1])); + + std::string mangled_add_int; + std::string mangled_add_double; + compat::maybeMangleDeclName(Add_int, mangled_add_int); + compat::maybeMangleDeclName(Add_double, mangled_add_double); + + // CppAPIType:: gives us the specific function pointer types + CppAPIType::Demangle DemangleFn = + reinterpret_cast(dlGetProcAddress("Demangle")); + CppAPIType::GetQualifiedCompleteName GetQualifiedCompleteNameFn = + reinterpret_cast( + dlGetProcAddress("GetQualifiedCompleteName")); + + std::string demangled_add_int = DemangleFn(mangled_add_int); + std::string demangled_add_double = DemangleFn(mangled_add_double); + + EXPECT_NE(demangled_add_int.find(GetQualifiedCompleteNameFn(Decls[0])), + std::string::npos); + EXPECT_NE(demangled_add_double.find(GetQualifiedCompleteNameFn(Decls[1])), + std::string::npos); +}