Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 5 additions & 37 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16)
project(CppJIT LANGUAGES C CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

Expand All @@ -11,7 +11,7 @@ include(GNUInstallDirs)
# This option won't make a lot of sense since we only ship the shared library in site-packages
# Perhaps this should permanently be OFF and users can build their own CppInterOp if they want to run the tests?
option(CPPJIT_ENABLE_CPPINTEROP_TESTS "enable CppInterOp tests" OFF)
set(CPPINTEROP_GIT_TAG "main" CACHE STRING "")
set(CPPINTEROP_GIT_TAG "a438c13316a8c1917cb86e00d5b0d95e6aee75f5" CACHE STRING "")

set(Python_FIND_VIRTUALENV ONLY)
find_package(Python COMPONENTS Interpreter Development)
Expand Down Expand Up @@ -78,41 +78,9 @@ else()
set(CPPINTEROP_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cppjit/cppyy_backend")
endif()

set(_interop_cmake_args
-DLLVM_DIR=${LLVM_DIR}
-DCPPINTEROP_USE_REPL=ON
-DCPPINTEROP_USE_CLING=OFF
-DCPPINTEROP_ENABLE_TESTING=${CPPJIT_ENABLE_CPPINTEROP_TESTS}
-DBUILD_SHARED_LIBS=ON
-DCMAKE_INSTALL_PREFIX=${CPPINTEROP_INSTALL_DIR}
-DCMAKE_INSTALL_LIBDIR=lib
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_STANDARD=17
)
if(Clang_DIR)
list(APPEND _interop_cmake_args -DClang_DIR=${Clang_DIR})
endif()
if(CMAKE_C_COMPILER)
list(APPEND _interop_cmake_args -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER})
endif()
if(CMAKE_CXX_COMPILER)
list(APPEND _interop_cmake_args -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER})
endif()

ExternalProject_Add(CppInterOp
GIT_REPOSITORY https://github.com/compiler-research/CppInterOp.git
GIT_TAG ${CPPINTEROP_GIT_TAG}
GIT_SHALLOW ON
PREFIX "${CMAKE_BINARY_DIR}/CppInterOp"
CMAKE_ARGS ${_interop_cmake_args}
BUILD_BYPRODUCTS
"${CPPINTEROP_INSTALL_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}"
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON
LOG_INSTALL ON
LOG_OUTPUT_ON_FAILURE ON
)
# Include cmake for CppInterOp config and build using ExternalProject.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/AddCppInterOp.cmake)
cppjit_add_cppinterop()

# this libcppyy.so merges both CPyCppyy and clingwrapper
file(GLOB CPYCPPYY_SOURCES CONFIGURE_DEPENDS src/CPyCppyy/src/*.cxx)
Expand Down
75 changes: 75 additions & 0 deletions cmake/AddCppInterOp.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Configures the CppInterOp ExternalProject. Default backend is clang-repl;
# CPPJIT_USE_CLING builds CppInterOp against a provided cling build
include_guard(GLOBAL)
include(ExternalProject)

# Developer toggle: Cling from either ROOT or standalone supplies LLVM_DIR/Clang_DIR
option(CPPJIT_USE_CLING "Build CppInterOp against a prebuilt Cling C++ Interpreter (ROOT)" OFF)
mark_as_advanced(CPPJIT_USE_CLING)

set(Cling_DIR "" CACHE PATH "Cling CMake config dir; derived from LLVM_DIR when empty (cling mode only)")
# Surface Cling_DIR in ccmake/GUI only when cling mode is on.
if(CPPJIT_USE_CLING)
mark_as_advanced(CLEAR Cling_DIR)
else()
mark_as_advanced(Cling_DIR)
endif()

function(cppjit_add_cppinterop)
set(_args
-DLLVM_DIR=${LLVM_DIR}
-DCPPINTEROP_ENABLE_TESTING=${CPPJIT_ENABLE_CPPINTEROP_TESTS}
-DBUILD_SHARED_LIBS=ON
-DCMAKE_INSTALL_PREFIX=${CPPINTEROP_INSTALL_DIR}
-DCMAKE_INSTALL_LIBDIR=lib
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_STANDARD=17
)

if(CPPJIT_USE_CLING)
set(_cling_dir "${Cling_DIR}")
if(NOT _cling_dir)
# LLVM_DIR is <prefix>/lib/cmake/llvm; cling config sits at the
# sibling <prefix>/lib/cmake/cling (setup-llvm flavor:cling layout).
get_filename_component(_prefix "${LLVM_DIR}" DIRECTORY) # <prefix>/lib/cmake
get_filename_component(_prefix "${_prefix}" DIRECTORY) # <prefix>/lib
get_filename_component(_prefix "${_prefix}" DIRECTORY) # <prefix>
set(_cling_dir "${_prefix}/lib/cmake/cling")
endif()
message(STATUS "CppInterOp backend: Cling (found at ${_cling_dir})")
list(APPEND _args
-DCPPINTEROP_USE_CLING=ON
-DCPPINTEROP_USE_REPL=OFF
-DCling_DIR=${_cling_dir}
)
else()
list(APPEND _args
-DCPPINTEROP_USE_REPL=ON
-DCPPINTEROP_USE_CLING=OFF
)
endif()

if(Clang_DIR)
list(APPEND _args -DClang_DIR=${Clang_DIR})
endif()
if(CMAKE_C_COMPILER)
list(APPEND _args -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER})
endif()
if(CMAKE_CXX_COMPILER)
list(APPEND _args -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER})
endif()

ExternalProject_Add(CppInterOp
GIT_REPOSITORY https://github.com/compiler-research/CppInterOp.git
GIT_TAG ${CPPINTEROP_GIT_TAG}
PREFIX "${CMAKE_BINARY_DIR}/CppInterOp"
CMAKE_ARGS ${_args}
BUILD_BYPRODUCTS
"${CPPINTEROP_INSTALL_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}"
LOG_DOWNLOAD ON
LOG_CONFIGURE ON
LOG_BUILD ON
LOG_INSTALL ON
LOG_OUTPUT_ON_FAILURE ON
)
endfunction()
2 changes: 1 addition & 1 deletion python/cppjit/cppyy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def cppexec(stmt):
def evaluate(input):
box = gbl.Cpp.Evaluate(input)
# Truthy sentinel: skips Box::convertTo's UB-on-K_Unspecified arm.
if box.getKind() == gbl.CppImpl.Box.K_Unspecified:
if box.getKind() == gbl.Cpp.Box.K_Unspecified:
return ~0
return box.convertTo['long']()

Expand Down
24 changes: 16 additions & 8 deletions src/CPyCppyy/include/CPyCppyy/API.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,24 @@
#define CPYCPPYY_VERSION_HEX 0x011200

// Cppyy types
#ifndef CPYCPPYY_INTERNAL

namespace Cpp {
struct DeclRef;
struct TypeRef;
struct FuncRef;
struct ObjectRef;
} // namespace Cpp

namespace Cppyy {
typedef void* TCppScope_t;
typedef TCppScope_t TCppType_t;
typedef void* TCppEnum_t;
typedef void* TCppObject_t;
typedef void* TCppMethod_t;

typedef size_t TCppIndex_t;
typedef void* TCppFuncAddr_t;
typedef Cpp::DeclRef TCppScope_t;
typedef Cpp::TypeRef TCppType_t;
typedef Cpp::ObjectRef TCppObject_t;
typedef Cpp::FuncRef TCppMethod_t;
typedef size_t TCppIndex_t;
typedef void* TCppFuncAddr_t;
} // namespace Cppyy
#endif

// Bindings
#include "CPyCppyy/CommonDefs.h"
Expand Down
25 changes: 2 additions & 23 deletions src/CPyCppyy/src/API.cxx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Bindings
#include "CPyCppyy.h"
#include "Cppyy.h"
#define CPYCPPYY_INTERNAL 1
#include "CPyCppyy/API.h"
#undef CPYCPPYY_INTERNAL
Expand Down Expand Up @@ -47,22 +48,10 @@ static bool Initialize()

if (!Py_IsInitialized()) {
// this happens if Cling comes in first
#if PY_VERSION_HEX < 0x03020000
PyEval_InitThreads();
#endif
#if PY_VERSION_HEX < 0x03080000
Py_Initialize();
#else
PyConfig config;
PyConfig_InitPythonConfig(&config);
PyConfig_SetString(&config, &config.program_name, L"cppyy");
Py_InitializeFromConfig(&config);
#endif
#if PY_VERSION_HEX >= 0x03020000
#if PY_VERSION_HEX < 0x03090000
PyEval_InitThreads();
#endif
#endif

// try again to see if the interpreter is initialized
if (!Py_IsInitialized()) {
Expand All @@ -71,16 +60,6 @@ static bool Initialize()
return false;
}

// set the command line arguments on python's sys.argv
#if PY_VERSION_HEX < 0x03000000
char* argv[] = {const_cast<char*>("cppyy")};
#elif PY_VERSION_HEX < 0x03080000
wchar_t* argv[] = {const_cast<wchar_t*>(L"cppyy")};
#endif
#if PY_VERSION_HEX < 0x03080000
PySys_SetArgv(sizeof(argv)/sizeof(argv[0]), argv);
#endif

// force loading of the cppyy module
PyRun_SimpleString(const_cast<char*>("import cppyy"));
}
Expand Down Expand Up @@ -112,7 +91,7 @@ std::string CPyCppyy::Instance_GetScopedFinalName(PyObject* pyobject)
return "";
}

Cppyy::TCppType_t pyobjectClass = ((CPPInstance *)pyobject)->ObjectIsA();
Cppyy::TCppScope_t pyobjectClass = ((CPPInstance *)pyobject)->ObjectIsA();
return Cppyy::GetScopedFinalName(pyobjectClass);
}

Expand Down
9 changes: 2 additions & 7 deletions src/CPyCppyy/src/CPPClassMethod.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@


//- public members --------------------------------------------------------------
PyObject* CPyCppyy::CPPClassMethod::Call(CPPInstance*&
#if PY_VERSION_HEX >= 0x03080000
self
#endif
, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, CallContext* ctxt)
PyObject *CPyCppyy::CPPClassMethod::Call(CPPInstance *&self, CPyCppyy_PyArgs_t args,
size_t nargsf, PyObject *kwds, CallContext *ctxt)
{
// preliminary check in case keywords are accidently used (they are ignored otherwise)
if (kwds && ((PyDict_Check(kwds) && PyDict_Size(kwds)) ||
Expand All @@ -23,7 +20,6 @@ PyObject* CPyCppyy::CPPClassMethod::Call(CPPInstance*&
return nullptr;

// translate the arguments
#if PY_VERSION_HEX >= 0x03080000
// TODO: The following is not robust and should be revisited e.g. by making CPPOverloads
// that have only CPPClassMethods be true Python classmethods? Note that the original
// implementation wasn't 100% correct either (e.g. static size() mapped to len()).
Expand All @@ -40,7 +36,6 @@ PyObject* CPyCppyy::CPPClassMethod::Call(CPPInstance*&
nargsf -= 1;
}
}
#endif

if (!this->ConvertAndSetArgs(args, nargsf, ctxt))
return nullptr;
Expand Down
17 changes: 1 addition & 16 deletions src/CPyCppyy/src/CPPConstructor.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ PyObject* CPyCppyy::CPPConstructor::Call(CPPInstance*& self,
// mark as actual to prevent needless auto-casting and register on its class
self->fFlags |= CPPInstance::kIsActual;
if (!(((CPPClass*)Py_TYPE(self))->fFlags & CPPScope::kIsSmart))
MemoryRegulator::RegisterPyObject(self, (Cppyy::TCppObject_t)address);
MemoryRegulator::RegisterPyObject(self, Cppyy::TCppObject_t((void*)address));

// handling smart types this way is deeply fugly, but if CPPInstance sets the proper
// types in op_new first, then the wrong init is called
Expand Down Expand Up @@ -225,7 +225,6 @@ PyObject* CPyCppyy::CPPMultiConstructor::Call(CPPInstance*& self,
// TODO: this way of forwarding is expensive as the loop is external to this call;
// it would be more efficient to have the argument handling happen beforehand

#if PY_VERSION_HEX >= 0x03080000
// fetch self, verify, and put the arguments in usable order (if self is not handled
// first, arguments can not be reordered with sentinels in place)
PyCallArgs cargs{self, argsin, nargsf, kwds};
Expand All @@ -244,11 +243,6 @@ PyObject* CPyCppyy::CPPMultiConstructor::Call(CPPInstance*& self,
// copy out self as it may have been updated
self = cargs.fSelf;

#else
PyObject* args = argsin;
Py_INCREF(args);
#endif

if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args)) { // case 0. falls through
Py_ssize_t nArgs = PyTuple_GET_SIZE(args);

Expand Down Expand Up @@ -311,20 +305,13 @@ PyObject* CPyCppyy::CPPMultiConstructor::Call(CPPInstance*& self,
}
}

#if PY_VERSION_HEX < 0x03080000
Py_ssize_t
#endif
nargs = PyTuple_GET_SIZE(args);

#if PY_VERSION_HEX >= 0x03080000
// now unroll the new args tuple into a vector of objects
auto argsu = std::unique_ptr<PyObject*[]>{new PyObject*[nargs]};
for (Py_ssize_t i = 0; i < nargs; ++i)
argsu[i] = PyTuple_GET_ITEM(args, i);
CPyCppyy_PyArgs_t _args = argsu.get();
#else
CPyCppyy_PyArgs_t _args = args;
#endif

PyObject* result = CPPConstructor::Call(self, _args, nargs, kwds, ctxt);
Py_DECREF(args);
Expand All @@ -339,11 +326,9 @@ PyObject* CPyCppyy::CPPAbstractClassConstructor::Call(CPPInstance*& self,
{
// do not allow instantiation of abstract classes
if ((self && GetScope() != self->ObjectIsA()
#if PY_VERSION_HEX >= 0x03080000
) || (!self && !(ctxt->fFlags & CallContext::kFromDescr) && \
CPyCppyy_PyArgs_GET_SIZE(args, nargsf) && CPPInstance_Check(args[0]) && \
GetScope() != ((CPPInstance*)args[0])->ObjectIsA()
#endif
)) {
// happens if a dispatcher is inserted; allow constructor call
return CPPConstructor::Call(self, args, nargsf, kwds, ctxt);
Expand Down
30 changes: 9 additions & 21 deletions src/CPyCppyy/src/CPPDataMember.cxx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Bindings
#include "CPyCppyy.h"
#include "CPyCppyy/Reflex.h"
#include "Cppyy.h"
#include "PyStrings.h"
#include "CPPDataMember.h"
#include "CPPInstance.h"
Expand Down Expand Up @@ -193,7 +194,7 @@ static CPPDataMember* dm_new(PyTypeObject* pytype, PyObject*, PyObject*)
dm->fOffset = 0;
dm->fFlags = 0;
dm->fConverter = nullptr;
dm->fEnclosingScope = 0;
dm->fEnclosingScope = nullptr;
dm->fDescription = nullptr;
dm->fDoc = nullptr;

Expand Down Expand Up @@ -297,25 +298,12 @@ PyTypeObject CPPDataMember_Type = {
0, // tp_mro
0, // tp_cache
0, // tp_subclasses
0 // tp_weaklist
#if PY_VERSION_HEX >= 0x02030000
, 0 // tp_del
#endif
#if PY_VERSION_HEX >= 0x02060000
, 0 // tp_version_tag
#endif
#if PY_VERSION_HEX >= 0x03040000
, 0 // tp_finalize
#endif
#if PY_VERSION_HEX >= 0x03080000
, 0 // tp_vectorcall
#endif
#if PY_VERSION_HEX >= 0x030c0000
, 0 // tp_watched
#endif
#if PY_VERSION_HEX >= 0x030d0000
, 0 // tp_versions_used
#endif
0, // tp_weaklist
0, // tp_del
0, // tp_version_tag
0, // tp_finalize
0 // tp_vectorcall
CPYCPPYY_PYTYPE_TAIL
};

} // namespace CPyCppyy
Expand Down Expand Up @@ -416,7 +404,7 @@ void* CPyCppyy::CPPDataMember::GetAddress(CPPInstance* pyobj)

// the proxy's internal offset is calculated from the enclosing class
ptrdiff_t offset = 0;
Cppyy::TCppType_t oisa = pyobj->ObjectIsA();
Cppyy::TCppScope_t oisa = pyobj->ObjectIsA();
if (oisa != fEnclosingScope)
offset = Cppyy::GetBaseOffset(oisa, fEnclosingScope, obj, 1 /* up-cast */);

Expand Down
Loading
Loading