From b8e6c082078f590434c55d88529d641936350ace Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 15 Oct 2025 14:07:22 +0200 Subject: [PATCH 1/5] 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 0ab686dc67677f2da239c7d72beecaa8cdf1bfe5 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Wed, 15 Oct 2025 14:10:14 +0200 Subject: [PATCH 2/5] [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 6e28633c1..3a14c9f41 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); +} From 4cd8f9a03970ad2653da63f6230be249367f5604 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Thu, 11 Dec 2025 14:50:19 +0100 Subject: [PATCH 3/5] Latest API changes, forward declare ValueKind --- include/CppInterOp/CppInterOpDispatch.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/CppInterOp/CppInterOpDispatch.h b/include/CppInterOp/CppInterOpDispatch.h index 20ca4c09c..8c3ebe3a8 100644 --- a/include/CppInterOp/CppInterOpDispatch.h +++ b/include/CppInterOp/CppInterOpDispatch.h @@ -55,7 +55,7 @@ extern "C" CPPINTEROP_API void ( DO(IsNamespace) \ DO(ObjToString) \ DO(GetQualifiedCompleteName) \ - DO(IsLValueReferenceType) \ + DO(GetValueKind) \ DO(GetNonReferenceType) \ DO(IsEnumType) \ DO(GetIntegerTypeFromEnumType) \ @@ -178,6 +178,7 @@ using Operator = ::Cpp::Operator; using OperatorArity = ::Cpp::OperatorArity; using QualKind = ::Cpp::QualKind; using TemplateArgInfo = ::Cpp::TemplateArgInfo; +using ValueKind = ::Cpp::ValueKind; using JitCall = ::Cpp::JitCall; } // end namespace CppDispatch @@ -390,7 +391,7 @@ using GetPointeeType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); using IsReferenceType = bool (*)(Cpp::TCppType_t type); -using IsLValueReferenceType = bool (*)(Cpp::TCppType_t type); +using GetValueKind = Cpp::ValueKind (*)(Cpp::TCppType_t type); using IsRValueReferenceType = bool (*)(Cpp::TCppType_t type); From 2ce71ec3b19c7fa5d0cb49886bc3308de8c73322 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Thu, 11 Dec 2025 14:50:53 +0100 Subject: [PATCH 4/5] Use std::unordered_map directly --- lib/CppInterOp/CppInterOpDispatch.cpp | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/lib/CppInterOp/CppInterOpDispatch.cpp b/lib/CppInterOp/CppInterOpDispatch.cpp index 40f227f16..d4062cf6b 100644 --- a/lib/CppInterOp/CppInterOpDispatch.cpp +++ b/lib/CppInterOp/CppInterOpDispatch.cpp @@ -8,7 +8,7 @@ #include -static const FunctionEntry api_function_table[] = { +static const std::unordered_map INTEROP_FUNCTIONS = { {"GetInterpreter", (__CPP_FUNC)Cpp::GetInterpreter}, {"CreateInterpreter", (__CPP_FUNC)Cpp::CreateInterpreter}, {"Process", (__CPP_FUNC)Cpp::Process}, @@ -20,7 +20,7 @@ static const FunctionEntry api_function_table[] = { {"IsNamespace", (__CPP_FUNC)Cpp::IsNamespace}, {"ObjToString", (__CPP_FUNC)Cpp::ObjToString}, {"GetQualifiedCompleteName", (__CPP_FUNC)Cpp::GetQualifiedCompleteName}, - {"IsLValueReferenceType", (__CPP_FUNC)Cpp::IsLValueReferenceType}, + {"GetValueKind", (__CPP_FUNC)Cpp::GetValueKind}, {"GetNonReferenceType", (__CPP_FUNC)Cpp::GetNonReferenceType}, {"IsEnumType", (__CPP_FUNC)Cpp::IsEnumType}, {"GetIntegerTypeFromEnumType", (__CPP_FUNC)Cpp::GetIntegerTypeFromEnumType}, @@ -131,27 +131,12 @@ static const FunctionEntry api_function_table[] = { {"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 */ + {"EnableDebugOutput", (__CPP_FUNC)Cpp::EnableDebugOutput} }; 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; + auto it = INTEROP_FUNCTIONS.find(funcName); + return (it != INTEROP_FUNCTIONS.end()) ? it->second : nullptr; } void (*CppGetProcAddress(const unsigned char* procName))(void) { From 3b9e2b45c47df859d05e0a09d94f1bb99a086093 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 15 Dec 2025 17:09:09 +0100 Subject: [PATCH 5/5] Provide configureable `dlGetProcAddress` implementation, load API as part of the header --- include/CppInterOp/CppInterOpDispatch.h | 84 +++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/include/CppInterOp/CppInterOpDispatch.h b/include/CppInterOp/CppInterOpDispatch.h index 8c3ebe3a8..64145d2a3 100644 --- a/include/CppInterOp/CppInterOpDispatch.h +++ b/include/CppInterOp/CppInterOpDispatch.h @@ -17,13 +17,20 @@ #include #include +#include +#include +#include +#include + #include +// Configured by CMake, can be overridden by defining before including this +// header +#ifndef CPPINTEROP_LIBRARY_PATH +#define CPPINTEROP_LIBRARY_PATH "@CPPINTEROP_LIBRARY_PATH@" +#endif + 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. @@ -518,4 +525,73 @@ using GetFunctionAddress = Cpp::TCppFuncAddr_t (*)(Cpp::TCppConstFunction_t func); } // end namespace CppAPIType +// TODO: implement overload that takes an existing opened DL handle +inline void* dlGetProcAddress(const char* name, + const char* customLibPath = nullptr) { + if (!name) + return nullptr; + + static std::once_flag loaded; + static void* handle = nullptr; + static void* (*getCppProcAddress)(const char*) = nullptr; + + std::call_once(loaded, [customLibPath]() { + // priority order: 1) custom path argument, or CPPINTEROP_LIBRARY_PATH via + // 2) cmake configured path 3) env vars + const char* libPath = customLibPath; + if (!libPath) { + libPath = std::getenv("CPPINTEROP_LIBRARY_PATH"); + } + if (!libPath || libPath[0] == '\0') { + libPath = CPPINTEROP_LIBRARY_PATH; + } + + handle = dlopen(libPath, RTLD_LOCAL | RTLD_NOW); + if (!handle) { + std::cerr << "[CppInterOp] Failed to load library from " << libPath + << ": " << dlerror() << std::endl; + return; + } + + getCppProcAddress = reinterpret_cast( + dlsym(handle, "CppGetProcAddress")); + if (!getCppProcAddress) { + std::cerr << "[CppInterOp] Failed to find CppGetProcAddress: " + << dlerror() << std::endl; + dlclose(handle); + handle = nullptr; + } + }); + + return getCppProcAddress ? getCppProcAddress(name) : nullptr; +} +namespace CppDispatch { +FOR_EACH_CPP_FUNCTION(EXTERN_CPP_FUNC); +/// Initialize all CppInterOp API from the dynamically loaded library +/// (RTLD_LOCAL) \param[in] customLibPath Optional custom path to +/// libclangCppInterOp.so \returns true if initialization succeeded, false +/// otherwise +inline bool init_functions(const char* customLibPath = nullptr) { + // trigger library loading if custom path provided + if (customLibPath) { + void* test = dlGetProcAddress("GetInterpreter", customLibPath); + if (!test) { + std::cerr << "[CppInterOp] Failed to initialize with custom path: " + << customLibPath << std::endl; + return false; + } + } + + FOR_EACH_CPP_FUNCTION(LOAD_CPP_FUNCTION); + + // test to verify that critical (and consequently all) functions loaded + if (!GetInterpreter || !CreateInterpreter) { + std::cerr << "[CppInterOp] Failed to load critical functions" << std::endl; + return false; + } + + return true; +} +} // namespace CppDispatch + #endif // CPPINTEROP_CPPINTEROPDISPATCH_H