Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
cpyrtModule.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPDataMember.h"
6#include "CPPExcInstance.h"
7#include "CPPInstance.h"
8#include "CPPOverload.h"
9#include "CPPScope.h"
10#include "CallContext.h"
11#include "Converters.h"
12#include "CustomPyTypes.h"
13#include "LowLevelViews.h"
14#include "MemoryRegulator.h"
15#include "ProxyWrappers.h"
16#include "PyStrings.h"
17#include "TemplateProxy.h"
18#include "TupleOfInstances.h"
19#include "TypeManip.h"
20#include "Utility.h"
21#include "cppjit_interop.h"
22#include <unordered_map>
23
24#define CPYRT_INTERNAL 1
25#include "cpyrt/DispatchPtr.h"
26namespace cppjit::cpyrt {
27void* Instance_AsVoidPtr(PyObject* pyobject);
28PyObject* Instance_FromVoidPtr(void* addr, const std::string& classname,
29 bool python_owns = false);
30} // namespace cppjit::cpyrt
31#undef CPYRT_INTERNAL
32
33// Standard
34#include <algorithm>
35#include <iostream>
36#include <map>
37#include <sstream>
38#include <string>
39#include <unordered_set>
40#include <utility>
41#include <vector>
42
43std::unordered_map<interop::TCppType_t, interop::TCppType_t> TypeReductionMap;
44
45//- data -----------------------------------------------------------------------
47 return PyUnicode_FromString("nullptr");
48}
49
51 Py_FatalError("deallocating nullptr");
52}
53
54static int nullptr_nonzero(PyObject*) { return 0; }
55
56static PyNumberMethods nullptr_as_number = {
57 0,
58 0,
59 0,
60 0,
61 0,
62 0,
63 0,
64 0,
65 0,
66 (inquiry)nullptr_nonzero, // tp_nonzero (nb_bool in p3)
67 0,
68 0,
69 0,
70 0,
71 0,
72 0,
73 0,
74 0,
75 0,
76 0,
77 0,
78 0,
79 0,
80 0,
81 0,
82 0,
83 0,
84 0,
85 0,
86 0, // nb_floor_divide
87 0, // nb_true_divide
88 0,
89 0,
90 0, // nb_index
91 0, // nb_matrix_multiply
92 0 // nb_inplace_matrix_multiply
93};
94
95static PyTypeObject PyNullPtr_t_Type = {
96 PyVarObject_HEAD_INIT(&PyType_Type, 0) "nullptr_t", // tp_name
97 sizeof(PyObject), // tp_basicsize
98 0, // tp_itemsize
99 nullptr_dealloc, // tp_dealloc (never called)
100 0,
101 0,
102 0,
103 0,
104 nullptr_repr, // tp_repr
105 &nullptr_as_number, // tp_as_number
106 0,
107 0,
108#if PY_VERSION_HEX >= 0x030d0000
109 (hashfunc)Py_HashPointer, // tp_hash
110#else
111 (hashfunc)_Py_HashPointer, // tp_hash
112#endif
113 0,
114 0,
115 0,
116 0,
117 0,
118 Py_TPFLAGS_DEFAULT,
119 0,
120 0,
121 0,
122 0,
123 0,
124 0,
125 0,
126 0,
127 0,
128 0,
129 0,
130 0,
131 0,
132 0,
133 0,
134 0,
135 0,
136 0,
137 0,
138 0,
139 0,
140 0,
141 0,
142 0,
143 0,
144 0, // tp_del
145 0, // tp_version_tag
146 0, // tp_finalize
147 0 // tp_vectorcall
149
151 return PyUnicode_FromString("type default");
152}
153
155 Py_FatalError("deallocating default");
156}
157
158static PyTypeObject PyDefault_t_Type = {
159 PyVarObject_HEAD_INIT(&PyType_Type, 0) "default_t", // tp_name
160 sizeof(PyObject), // tp_basicsize
161 0, // tp_itemsize
162 default_dealloc, // tp_dealloc (never called)
163 0,
164 0,
165 0,
166 0,
167 default_repr, // tp_repr
168 0,
169 0,
170 0,
171#if PY_VERSION_HEX >= 0x030d0000
172 (hashfunc)Py_HashPointer, // tp_hash
173#else
174 (hashfunc)_Py_HashPointer, // tp_hash
175#endif
176 0,
177 0,
178 0,
179 0,
180 0,
181 Py_TPFLAGS_DEFAULT,
182 0,
183 0,
184 0,
185 0,
186 0,
187 0,
188 0,
189 0,
190 0,
191 0,
192 0,
193 0,
194 0,
195 0,
196 0,
197 0,
198 0,
199 0,
200 0,
201 0,
202 0,
203 0,
204 0,
205 0,
206 0,
207 0, // tp_del
208 0, // tp_version_tag
209 0, // tp_finalize
210 0 // tp_vectorcall
212
213namespace {
214
215struct {
216 PyObject_HEAD
217} _cpyrt_NullPtrStruct{PyObject_HEAD_INIT(&PyNullPtr_t_Type)};
218
219struct {
220 PyObject_HEAD
221} _cpyrt_DefaultStruct{PyObject_HEAD_INIT(&PyDefault_t_Type)};
222
223// TODO: refactor with Converters.cxx
224struct cpyrt_tagCDataObject { // non-public (but stable)
225 PyObject_HEAD char* b_ptr;
226 int b_needsfree;
227};
228
229} // unnamed namespace
230
231namespace cppjit::cpyrt {
232PyObject* gThisModule = nullptr;
234PyObject* gNullPtrObject = nullptr;
240std::unordered_set<interop::TCppScope_t> gPinnedTypes;
241std::ostringstream gCapturedError;
242std::streambuf* gOldErrorBuffer = nullptr;
243
244std::unordered_map<std::string, std::vector<PyObject*>>& pythonizations() {
245 static std::unordered_map<std::string, std::vector<PyObject*>> pyzMap;
246 return pyzMap;
247}
248} // namespace cppjit::cpyrt
249
250//- private helpers ------------------------------------------------------------
251namespace {
252
253using namespace cppjit::cpyrt;
254
255//----------------------------------------------------------------------------
256static PyObject* MakeCppTemplateClass(PyObject* /* self */, PyObject* args) {
257 // Create a binding for a templated class instantiation.
258
259 // args is class name + template arguments; build full instantiation
260 Py_ssize_t nArgs = PyTuple_GET_SIZE(args);
261 if (nArgs < 2) {
262 PyErr_Format(PyExc_TypeError,
263 "too few arguments for template instantiation");
264 return nullptr;
265 }
266 PyObject* cppscope = PyTuple_GET_ITEM(args, 0);
267 void* tmpl = PyLong_AsVoidPtr(cppscope);
268
269 // build "< type, type, ... >" part of class name (modifies pyname)
270 std::vector<Cpp::TemplateArgInfo> types =
271 Utility::GetTemplateArgsTypes(cppscope, args, nullptr, Utility::kNone, 1);
272 if (PyErr_Occurred())
273 return nullptr;
274
276 interop::InstantiateTemplate(tmpl, types.data(), types.size());
277 for (Cpp::TemplateArgInfo i : types) {
278 if (i.m_IntegralValue)
279 std::free((void*)i.m_IntegralValue);
280 }
281
282 if (!scope) {
283 PyErr_Format(PyExc_TypeError,
284 "Template instantiation failed: '%s' with args: '%s\n'",
285 interop::GetScopedFinalName(tmpl).c_str(),
286 PyUnicode_AsUTF8(PyObject_Repr(args)));
287 return nullptr;
288 }
289
290 return CreateScopeProxy(scope);
291}
292
293//----------------------------------------------------------------------------
294static char* GCIA_kwlist[] = {(char*)"instance", (char*)"field", (char*)"byref",
295 NULL};
296static void* GetCPPInstanceAddress(const char* fname, PyObject* args,
297 PyObject* kwds) {
298 // Helper to get the address (address-of-address) of various object proxy
299 // types.
300 CPPInstance* pyobj = 0;
301 PyObject* pyname = 0;
302 int byref = 0;
303 if (PyArg_ParseTupleAndKeywords(args, kwds, const_cast<char*>("O|O!b"),
304 GCIA_kwlist, &pyobj, &PyUnicode_Type, &pyname,
305 &byref)) {
306
307 if (CPPInstance_Check(pyobj)) {
308 if (pyname != 0) {
309 // locate property proxy for offset info
310 CPPDataMember* pyprop = nullptr;
311
312 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)pyobj);
313 PyObject* dict = PyObject_GetAttr(pyclass, PyStrings::gDict);
314 pyprop = (CPPDataMember*)PyObject_GetItem(dict, pyname);
315 Py_DECREF(dict);
316
317 if (CPPDataMember_Check(pyprop)) {
318 // this is an address of a value (i.e. &myobj->prop)
319 void* addr = (void*)pyprop->GetAddress(pyobj);
320 Py_DECREF(pyprop);
321 return addr;
322 }
323
324 Py_XDECREF(pyprop);
325
326 PyErr_Format(PyExc_TypeError, "%s is not a valid data member",
327 PyUnicode_AsUTF8(pyname));
328 return nullptr;
329 }
330
331 // this is an address of an address (i.e. &myobj, with myobj of type
332 // MyObj*) note that the return result may be null
333 if (!byref)
334 return ((CPPInstance*)pyobj)->GetObject();
335 return &((CPPInstance*)pyobj)->GetObjectRaw();
336
337 } else if (PyUnicode_Check(pyobj)) {
338 // special cases for access to the cpyrt API
339 std::string req = PyUnicode_AsUTF8((PyObject*)pyobj);
340 if (req == "Instance_AsVoidPtr")
341 return (void*)&Instance_AsVoidPtr;
342 else if (req == "Instance_FromVoidPtr")
343 return (void*)&Instance_FromVoidPtr;
344 }
345 }
346
347 if (!PyErr_Occurred())
348 PyErr_Format(PyExc_ValueError, "invalid argument for %s", fname);
349 return nullptr;
350}
351
352//----------------------------------------------------------------------------
353static PyObject* addressof(PyObject* /* dummy */, PyObject* args,
354 PyObject* kwds) {
355 // Return object proxy address as a value (cppjit-style), or the same for an
356 // array.
357 void* addr = GetCPPInstanceAddress("addressof", args, kwds);
358 if (addr)
359 return PyLong_FromLongLong((intptr_t)addr);
360 else if (!PyErr_Occurred()) {
361 return PyLong_FromLong(0);
362 } else if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args) == 1) {
363 PyErr_Clear();
364 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
365
366 // nullptr special case
367 if (arg0 == gNullPtrObject ||
368 (PyLong_Check(arg0) && PyLong_AsLong(arg0) == 0))
369 return PyLong_FromLong(0);
370
371 // overload if unambiguous
372 if (CPPOverload_CheckExact(arg0)) {
373 const auto& methods = ((CPPOverload*)arg0)->fMethodInfo->fMethods;
374 if (methods.size() != 1) {
375 PyErr_SetString(PyExc_TypeError, "overload is not unambiguous");
376 return nullptr;
377 }
378
379 interop::TCppFuncAddr_t caddr = methods[0]->GetFunctionAddress();
380 return PyLong_FromLongLong((intptr_t)caddr);
381 }
382
383 // C functions (incl. ourselves)
384 if (PyCFunction_Check(arg0)) {
385 void* caddr = (void*)PyCFunction_GetFunction(arg0);
386 return PyLong_FromLongLong((intptr_t)caddr);
387 }
388
389 // LowLevelViews
390 if (LowLevelView_CheckExact(arg0)) {
391 auto* llv = (LowLevelView*)arg0;
392 return PyLong_FromLongLong((intptr_t)llv->get_buf());
393 }
394
395 // final attempt: any type of buffer
396 Utility::GetBuffer(arg0, '*', 1, addr, false);
397 if (addr)
398 return PyLong_FromLongLong((intptr_t)addr);
399 }
400
401 // error message if not already set
402 if (!PyErr_Occurred()) {
403 if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args)) {
404 PyObject* str = PyObject_Str(PyTuple_GET_ITEM(args, 0));
405 if (str && PyUnicode_Check(str))
406 PyErr_Format(PyExc_TypeError, "unknown object %s",
407 PyUnicode_AsUTF8(str));
408 else
409 PyErr_Format(PyExc_TypeError, "unknown object at %p",
410 (void*)PyTuple_GET_ITEM(args, 0));
411 Py_XDECREF(str);
412 }
413 }
414 return nullptr;
415}
416
417//----------------------------------------------------------------------------
418static PyObject* AsCObject(PyObject* /* unused */, PyObject* args,
419 PyObject* kwds) {
420 // Return object proxy as an opaque CObject.
421 void* addr = GetCPPInstanceAddress("as_cobject", args, kwds);
422 if (addr)
423 return PyCapsule_New((void*)addr, nullptr, nullptr);
424 return nullptr;
425}
426
427//----------------------------------------------------------------------------
428static PyObject* AsCapsule(PyObject* /* unused */, PyObject* args,
429 PyObject* kwds) {
430 // Return object proxy as an opaque PyCapsule.
431 void* addr = GetCPPInstanceAddress("as_capsule", args, kwds);
432 if (addr)
433 return PyCapsule_New(addr, nullptr, nullptr);
434 return nullptr;
435}
436
437//----------------------------------------------------------------------------
438static PyObject* AsCTypes(PyObject* /* unused */, PyObject* args,
439 PyObject* kwds) {
440 // Return object proxy as a ctypes c_void_p
441 void* addr = GetCPPInstanceAddress("as_ctypes", args, kwds);
442 if (!addr)
443 return nullptr;
444
445 // TODO: refactor code below with converters code
446 static PyTypeObject* ct_cvoidp = nullptr;
447 if (!ct_cvoidp) {
448 PyObject* ctmod = PyImport_ImportModule("ctypes"); // ref-count kept
449 if (!ctmod)
450 return nullptr;
451
452 ct_cvoidp = (PyTypeObject*)PyObject_GetAttrString(ctmod, "c_void_p");
453 Py_DECREF(ctmod);
454 if (!ct_cvoidp)
455 return nullptr;
456 Py_DECREF(ct_cvoidp); // module keeps a reference
457 }
458
459 PyObject* ref = ct_cvoidp->tp_new(ct_cvoidp, nullptr, nullptr);
460 *(void**)((cpyrt_tagCDataObject*)ref)->b_ptr = addr;
461 ((cpyrt_tagCDataObject*)ref)->b_needsfree = 0;
462 return ref;
463}
464
465//----------------------------------------------------------------------------
466static PyObject* AsMemoryView(PyObject* /* unused */, PyObject* pyobject) {
467 // Return a raw memory view on arrays of PODs.
468 if (!CPPInstance_Check(pyobject)) {
469 PyErr_SetString(PyExc_TypeError, "C++ object proxy expected");
470 return nullptr;
471 }
472
473 CPPInstance* pyobj = (CPPInstance*)pyobject;
474 interop::TCppScope_t klass = ((CPPClass*)Py_TYPE(pyobject))->fCppType;
475
476 Py_ssize_t array_len = pyobj->ArrayLength();
477
478 if (array_len < 0 || !interop::IsAggregate(klass)) {
479 PyErr_SetString(PyExc_TypeError,
480 "object is not a proxy to an array of PODs of known size");
481 return nullptr;
482 }
483
484 Py_buffer view;
485
486 view.obj = pyobject;
487 view.buf = pyobj->GetObject();
488 view.itemsize = interop::SizeOf(klass);
489 view.len = view.itemsize * array_len;
490 view.readonly = 0;
491 view.format = NULL; // i.e. "B" assumed
492 view.ndim = 1;
493 view.shape = NULL;
494 view.strides = NULL;
495 view.suboffsets = NULL;
496 view.internal = NULL;
497
498 return PyMemoryView_FromBuffer(&view);
499}
500
501//----------------------------------------------------------------------------
502static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) {
503 // From a long representing an address or a PyCapsule/CObject, bind to a
504 // class.
505 Py_ssize_t argc = PyTuple_GET_SIZE(args);
506 if (argc != 2) {
507 PyErr_Format(
508 PyExc_TypeError,
509 "bind_object takes 2 positional arguments but (" PY_SSIZE_T_FORMAT
510 " were given)",
511 argc);
512 return nullptr;
513 }
514
515 // convert 2nd argument first (used for both pointer value and instance cases)
516 interop::TCppScope_t cast_type = nullptr;
517 PyObject* arg1 = PyTuple_GET_ITEM(args, 1);
518 if (!PyUnicode_Check(arg1)) { // not string, then class
519 if (CPPScope_Check(arg1))
520 cast_type = ((CPPClass*)arg1)->fCppType;
521 else
522 arg1 = PyObject_GetAttr(arg1, PyStrings::gName);
523 } else
524 Py_INCREF(arg1);
525
526 if (!cast_type && arg1) {
527 cast_type = (interop::TCppScope_t)interop::GetScope(PyUnicode_AsUTF8(arg1));
528 Py_DECREF(arg1);
529 }
530
531 if (!cast_type) {
532 PyErr_SetString(
533 PyExc_TypeError,
534 "bind_object expects a valid class or class name as an argument");
535 return nullptr;
536 }
537
538 // next, convert the first argument, some pointer value or a pre-existing
539 // instance
540 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
541
542 if (CPPInstance_Check(arg0)) {
543 // if this instance's class has a relation to the requested one, calculate
544 // the offset, erase if from any caches, and update the pointer and type
545 CPPInstance* arg0_pyobj = (CPPInstance*)arg0;
546 interop::TCppScope_t cur_type =
547 arg0_pyobj->ObjectIsA(false /* check_smart */);
548
549 bool isPython = CPPScope_Check(arg1) &&
550 (((CPPClass*)arg1)->fFlags & CPPScope::kIsPython);
551
552 if (cur_type == cast_type && !isPython) {
553 Py_INCREF(arg0); // nothing to do
554 return arg0;
555 }
556
557 int direction = 0;
558 interop::TCppScope_t base = nullptr, derived = nullptr;
559 if (interop::IsSubclass(cast_type, cur_type)) {
560 derived = cast_type;
561 base = cur_type;
562 direction = -1; // down-cast
563 } else if (interop::IsSubclass(cur_type, cast_type)) {
564 base = cast_type;
565 derived = cur_type;
566 direction = 1; // up-cast
567 } else {
568 PyErr_SetString(
569 PyExc_TypeError,
570 "provided instance and provided target type are unrelated");
571 return nullptr;
572 }
573
574 interop::TCppObject_t address =
575 (interop::TCppObject_t)arg0_pyobj->GetObject();
576 ptrdiff_t offset =
577 interop::GetBaseOffset(derived, base, address, direction);
578
579 // it's debatable whether a new proxy should be created rather than updating
580 // the old, but changing the old object would be changing the behavior of
581 // all code that has a reference to it, which may not be the intention if
582 // the cast is on a C++ data member; this probably is the "least surprise"
583 // option
584
585 // ownership is taken over as needed, again following the principle of
586 // "least surprise" as most likely only the cast object will be retained
587 bool owns = arg0_pyobj->fFlags & CPPInstance::kIsOwner;
588
589 if (!isPython) {
590 // ordinary C++ class
592 interop::TCppObject_t((void*)((intptr_t)address.data + offset)),
593 cast_type, owns ? CPPInstance::kIsOwner : 0);
594 if (owns && pyobj)
595 arg0_pyobj->CppOwns();
596 return pyobj;
597
598 } else {
599 // rebinding to a Python-side class, create a fresh instance first to be
600 // able to perform a lookup of the original dispatch object and if found,
601 // return original
602 void* cast_address = (void*)((intptr_t)address.data + offset);
603 PyObject* pyobj =
604 ((PyTypeObject*)arg1)->tp_new((PyTypeObject*)arg1, nullptr, nullptr);
605 ((CPPInstance*)pyobj)->GetObjectRaw() = cast_address;
606
607 PyObject* dispproxy = cpyrt::GetScopeProxy(cast_type);
608 PyObject* res =
609 PyObject_CallMethodOneArg(dispproxy, PyStrings::gDispGet, pyobj);
610 /* Note: the resultant object is borrowed */
611 if (CPPInstance_Check(res) &&
612 ((CPPInstance*)res)->GetObject() == cast_address) {
613 ((CPPInstance*)pyobj)->CppOwns(); // make sure C++ object isn't deleted
614 Py_DECREF(pyobj); // on DECREF (is default, but still)
615 pyobj = res;
616 } else {
617 if (res)
618 Py_DECREF(res); // most likely Py_None
619 else
620 PyErr_Clear(); // should not happen
621 }
622 Py_DECREF(dispproxy);
623
624 if (pyobj && owns) {
625 arg0_pyobj->CppOwns();
626 ((CPPInstance*)pyobj)->PythonOwns();
627 }
628
629 return pyobj;
630 }
631 }
632
633 // not a pre-existing object; get the address and bind
634 void* addr = nullptr;
635 if (arg0 != gNullPtrObject) {
636 addr = PyCapsule_GetPointer(arg0, nullptr);
637 if (PyErr_Occurred()) {
638 PyErr_Clear();
639
640 addr = PyLong_AsVoidPtr(arg0);
641 if (PyErr_Occurred()) {
642 PyErr_Clear();
643
644 // last chance, perhaps it's a buffer/array (return from void*)
645 Py_ssize_t buflen =
646 Utility::GetBuffer(PyTuple_GetItem(args, 0), '*', 1, addr, false);
647 if (!addr || !buflen) {
648 PyErr_SetString(PyExc_TypeError,
649 "bind_object requires a CObject/Capsule, long "
650 "integer, buffer, or instance as first argument");
651 return nullptr;
652 }
653 }
654 }
655 }
656
657 bool do_cast = false;
658 if (kwds) {
659 PyObject* cast = PyDict_GetItemString(kwds, "cast");
660 do_cast = cast && PyObject_IsTrue(cast);
661 }
662
663 if (do_cast)
664 return BindCppObject(addr, cast_type);
665
666 return BindCppObjectNoCast(addr, cast_type);
667}
668
669//----------------------------------------------------------------------------
670static PyObject* BindValue(PyObject*, PyObject* args, PyObject* kwds) {
671 // Read through the converter for the given type name, at the given address.
672 static const char* kwlist[] = {(char*)"type_name", (char*)"address",
673 (char*)"dims", nullptr};
674
675 const char* type_name = nullptr;
676 PyObject* pyaddress = nullptr;
677 PyObject* pydims = nullptr;
678 if (!PyArg_ParseTupleAndKeywords(args, kwds, "sO|O:bind_value",
679 (char**)kwlist, &type_name, &pyaddress,
680 &pydims))
681 return nullptr;
682
683 void* address = PyLong_AsVoidPtr(pyaddress);
684 if (PyErr_Occurred())
685 return nullptr;
686
687 std::vector<cpyrt::dim_t> dims;
688 if (pydims && pydims != Py_None) {
689 PyObject* seq =
690 PySequence_Fast(pydims, "dims must be a sequence of integers");
691 if (!seq)
692 return nullptr;
693 Py_ssize_t ndim = PySequence_Fast_GET_SIZE(seq);
694 if (ndim == 0) {
695 Py_DECREF(seq);
696 PyErr_SetString(PyExc_ValueError, "dims must be a non-empty sequence");
697 return nullptr;
698 }
699 dims.reserve(ndim);
700 for (Py_ssize_t i = 0; i < ndim; ++i) {
702 (cpyrt::dim_t)PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(seq, i));
703 if (d == (cpyrt::dim_t)-1 && PyErr_Occurred()) {
704 Py_DECREF(seq);
705 return nullptr;
706 }
707 // an UNKNOWN_SIZE view would keep the address of the pointer slot on
708 // our expired stack frame as its data
709 if (d < 0) {
710 Py_DECREF(seq);
711 PyErr_SetString(PyExc_ValueError, "dims entries must be non-negative");
712 return nullptr;
713 }
714 dims.push_back(d);
715 }
716 Py_DECREF(seq);
717
718 // scalar converters ignore dims entirely and would silently read the
719 // pointer bits; only array and pointer type names can take a shape
720 const std::string cpd = cpyrt::TypeManip::compound(type_name);
721 if (cpd != "[]" && cpd != "*") {
722 PyErr_Format(PyExc_TypeError,
723 "dims given but \'%s\' is not an array or pointer type",
724 type_name);
725 return nullptr;
726 }
727 }
728
729 // an unresolvable name crashes deeper down in the type lookup instead of
730 // producing a nullptr converter, so validate first
731 const std::string resolvedType = interop::ResolveName(type_name);
732 const std::string baseType =
733 cpyrt::TypeManip::clean_type(resolvedType, false, true);
734 if (!interop::GetType(baseType, /* enable_slow_lookup */ true)) {
735 PyErr_Format(PyExc_TypeError, "no converter available for type \'%s\'",
736 type_name);
737 return nullptr;
738 }
739
741 type_name, cpyrt::Dimensions((cpyrt::dim_t)dims.size(), dims.data()));
742
743 // an array converter reads through the data pointer, so it wants the
744 // address of that pointer; a scalar converter wants the address of the
745 // value itself
747 dims.empty() ? cnv->FromMemory(address) : cnv->FromMemory(&address);
749
750 if (!result && !PyErr_Occurred())
751 PyErr_Format(PyExc_TypeError,
752 "failed to convert a value of type \'%s\' from memory",
753 type_name);
754
755 return result;
756}
757
758//----------------------------------------------------------------------------
759static PyObject* Move(PyObject*, PyObject* pyobject) {
760 // Prepare the given C++ object for moving.
761 if (!CPPInstance_Check(pyobject)) {
762 PyErr_SetString(PyExc_TypeError, "C++ object expected");
763 return nullptr;
764 }
765
766 ((CPPInstance*)pyobject)->fFlags |= CPPInstance::kIsRValue;
767 Py_INCREF(pyobject);
768 return pyobject;
769}
770
771//----------------------------------------------------------------------------
772static PyObject* AddPythonization(PyObject*, PyObject* args) {
773 // Remove a previously registered pythonizor from the given scope.
774 PyObject* pythonizor = nullptr;
775 const char* scope;
776 if (!PyArg_ParseTuple(args, const_cast<char*>("Os"), &pythonizor, &scope))
777 return nullptr;
778
779 if (!PyCallable_Check(pythonizor)) {
780 PyObject* pystr = PyObject_Str(pythonizor);
781 PyErr_Format(PyExc_TypeError, "given \'%s\' object is not callable",
782 PyUnicode_AsUTF8(pystr));
783 Py_DECREF(pystr);
784 return nullptr;
785 }
786
787 Py_INCREF(pythonizor);
788 pythonizations()[scope].push_back(pythonizor);
789
791}
792
793//----------------------------------------------------------------------------
794static PyObject* RemovePythonization(PyObject*, PyObject* args) {
795 // Remove a previously registered pythonizor from the given scope.
796 PyObject* pythonizor = nullptr;
797 const char* scope;
798 if (!PyArg_ParseTuple(args, const_cast<char*>("Os"), &pythonizor, &scope))
799 return nullptr;
800
801 auto& pyzMap = pythonizations();
802 auto p1 = pyzMap.find(scope);
803 if (p1 != pyzMap.end()) {
804 auto p2 = std::find(p1->second.begin(), p1->second.end(), pythonizor);
805 if (p2 != p1->second.end()) {
806 p1->second.erase(p2);
808 }
809 }
810
812}
813
814//----------------------------------------------------------------------------
815static PyObject* PinType(PyObject*, PyObject* pyclass) {
816 // Add a pinning so that objects of type `derived' are interpreted as
817 // objects of type `base'.
818 if (!CPPScope_Check(pyclass)) {
819 PyErr_SetString(PyExc_TypeError, "C++ class expected");
820 return nullptr;
821 }
822
823 gPinnedTypes.insert(((CPPClass*)pyclass)->fCppType);
824
826}
827
828//----------------------------------------------------------------------------
829static PyObject* AddTypeReducer(PyObject*, PyObject* args) {
830 // Add a type reducer to map type2 to type2 on function returns.
831 const char *reducable, *reduced;
832 if (!PyArg_ParseTuple(args, const_cast<char*>("ss"), &reducable, &reduced))
833 return nullptr;
834
835 interop::TCppType_t reducable_type =
837 interop::TCppType_t reduced_type =
839 TypeReductionMap[reducable_type] = reduced_type;
840
842}
843
844#define DEFINE_CALL_POLICY_TOGGLE(name, flagname) \
845 static PyObject* name(PyObject*, PyObject* args) { \
846 PyObject* enabled = 0; \
847 if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &enabled)) \
848 return nullptr; \
849 \
850 if (CallContext::SetGlobalPolicy(CallContext::flagname, \
851 PyObject_IsTrue(enabled))) { \
852 Py_RETURN_TRUE; \
853 } \
854 \
855 Py_RETURN_FALSE; \
856 }
857
858DEFINE_CALL_POLICY_TOGGLE(SetHeuristicMemoryPolicy, kUseHeuristics);
859DEFINE_CALL_POLICY_TOGGLE(SetImplicitSmartPointerConversion,
860 kImplicitSmartPtrConversion);
861DEFINE_CALL_POLICY_TOGGLE(SetGlobalSignalPolicy, kProtected);
862
863//----------------------------------------------------------------------------
864static PyObject* SetOwnership(PyObject*, PyObject* args) {
865 // Set the ownership (True is python-owns) for the given object.
866 CPPInstance* pyobj = nullptr;
867 PyObject* pykeep = nullptr;
868 if (!PyArg_ParseTuple(args, const_cast<char*>("O!O!"), &CPPInstance_Type,
869 (void*)&pyobj, &PyLong_Type, &pykeep))
870 return nullptr;
871
872 (bool)PyLong_AsLong(pykeep) ? pyobj->PythonOwns() : pyobj->CppOwns();
873
875}
876
877//----------------------------------------------------------------------------
878static PyObject* AddSmartPtrType(PyObject*, PyObject* args) {
879 // Add a smart pointer to the list of known smart pointer types.
880 const char* type_name;
881 if (!PyArg_ParseTuple(args, const_cast<char*>("s"), &type_name))
882 return nullptr;
883
884 // interop::AddSmartPtrType(type_name);
885
887}
888
889//----------------------------------------------------------------------------
890static PyObject* BeginCaptureStderr(PyObject*, PyObject*) {
891 gOldErrorBuffer = std::cerr.rdbuf();
892 std::cerr.rdbuf(gCapturedError.rdbuf());
893
895}
896
897//----------------------------------------------------------------------------
898static PyObject* EndCaptureStderr(PyObject*, PyObject*) {
899 // restore old rdbuf and return captured result
900 std::cerr.rdbuf(gOldErrorBuffer);
901 gOldErrorBuffer = nullptr;
902
903 std::string capturedError = std::move(gCapturedError).str();
904
905 gCapturedError.str("");
906 gCapturedError.clear();
907
908 return Py_BuildValue("s", capturedError.c_str());
909}
910} // unnamed namespace
911
912//- data -----------------------------------------------------------------------
913//----------------------------------------------------------------------------
915 // release the GIL while waiting: the thread holding the interpreter lock
916 // may need Python to finish
917 Py_BEGIN_ALLOW_THREADS
919 Py_END_ALLOW_THREADS
921}
922
923//----------------------------------------------------------------------------
928
929static PyMethodDef gcpyrtMethods[] = {
930 {(char*)"CreateScopeProxy", (PyCFunction)cpyrt::CreateScopeProxy,
931 METH_VARARGS, (char*)"cppjit internal function"},
932 {(char*)"MakeCppTemplateClass", (PyCFunction)MakeCppTemplateClass,
933 METH_VARARGS, (char*)"cppjit internal function"},
934 {(char*)"_DestroyPyStrings", (PyCFunction)cpyrt::DestroyPyStrings,
935 METH_NOARGS, (char*)"cppjit internal function"},
936 {(char*)"addressof", (PyCFunction)addressof, METH_VARARGS | METH_KEYWORDS,
937 (char*)"Retrieve address of proxied object or field as a value."},
938 {(char*)"as_cobject", (PyCFunction)AsCObject, METH_VARARGS | METH_KEYWORDS,
939 (char*)"Retrieve address of proxied object or field in a CObject."},
940 {(char*)"as_capsule", (PyCFunction)AsCapsule, METH_VARARGS | METH_KEYWORDS,
941 (char*)"Retrieve address of proxied object or field in a PyCapsule."},
942 {(char*)"as_ctypes", (PyCFunction)AsCTypes, METH_VARARGS | METH_KEYWORDS,
943 (char*)"Retrieve address of proxied object or field in a ctypes "
944 "c_void_p."},
945 {(char*)"as_memoryview", (PyCFunction)AsMemoryView, METH_O,
946 (char*)"Represent an array of objects as raw memory."},
947 {(char*)"bind_object", (PyCFunction)BindObject,
948 METH_VARARGS | METH_KEYWORDS,
949 (char*)"Create an object of given type, from given address."},
950 {(char*)"bind_value", (PyCFunction)BindValue, METH_VARARGS | METH_KEYWORDS,
951 (char*)"Read a value of given type, from given address."},
952 {(char*)"move", (PyCFunction)Move, METH_O,
953 (char*)"Cast the C++ object to become movable."},
954 {(char*)"add_pythonization", (PyCFunction)AddPythonization, METH_VARARGS,
955 (char*)"Add a pythonizor."},
956 {(char*)"remove_pythonization", (PyCFunction)RemovePythonization,
957 METH_VARARGS, (char*)"Remove a pythonizor."},
958 {(char*)"_pin_type", (PyCFunction)PinType, METH_O,
959 (char*)"Install a type pinning."},
960 {(char*)"_add_type_reducer", (PyCFunction)AddTypeReducer, METH_VARARGS,
961 (char*)"Add a type reducer."},
962 {(char*)"SetHeuristicMemoryPolicy", (PyCFunction)SetHeuristicMemoryPolicy,
963 METH_VARARGS,
964 (char*)"Set the global memory policy, which affects object ownership when "
965 "objects are passed as function arguments."},
966 {(char*)"SetImplicitSmartPointerConversion",
967 (PyCFunction)SetImplicitSmartPointerConversion, METH_VARARGS,
968 (char*)"Enable or disable the implicit conversion to smart pointers in "
969 "function calls (on by default)."},
970 {(char*)"SetGlobalSignalPolicy", (PyCFunction)SetGlobalSignalPolicy,
971 METH_VARARGS,
972 (char*)"Set the global signal policy, which determines whether a jmp "
973 "address should be saved to return to after a "
974 "C++ segfault. In practical terms: trap signals in safe mode to "
975 "prevent interpreter abort."},
976 {(char*)"SetOwnership", (PyCFunction)SetOwnership, METH_VARARGS,
977 (char*)"Modify held C++ object ownership."},
978 {(char*)"AddSmartPtrType", (PyCFunction)AddSmartPtrType, METH_VARARGS,
979 (char*)"Add a smart pointer to the list of known smart pointer types."},
980 {(char*)"_begin_capture_stderr", (PyCFunction)BeginCaptureStderr,
981 METH_NOARGS, (char*)"Begin capturing stderr to a in memory buffer."},
982 {(char*)"_end_capture_stderr", (PyCFunction)EndCaptureStderr, METH_NOARGS,
983 (char*)"End capturing stderr and returns the captured buffer."},
984 {(char*)"_lock_interpreter", (PyCFunction)LockInterpreter, METH_NOARGS,
985 (char*)"Take the interpreter lock; pair with _unlock_interpreter."},
986 {(char*)"_unlock_interpreter", (PyCFunction)UnlockInterpreter,
987 METH_NOARGS, (char*)"Release the lock taken by _lock_interpreter."},
988 {nullptr, nullptr, 0, nullptr}};
989
993
994#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
995
996static int cpycppjitmodule_traverse(PyObject* m, visitproc visit, void* arg) {
997 Py_VISIT(GETSTATE(m)->error);
998 return 0;
999}
1000
1002 Py_CLEAR(GETSTATE(m)->error);
1003 return 0;
1004}
1005
1006static struct PyModuleDef moduledef = {
1007 PyModuleDef_HEAD_INIT, "libcppjit", nullptr,
1008 sizeof(struct module_state), gcpyrtMethods, nullptr,
1010
1011namespace cppjit::cpyrt {
1012
1013//----------------------------------------------------------------------------
1015 // Initialization of extension module libcppjit.
1016
1017 // load commonly used python strings
1019 return nullptr;
1020
1021 // setup this module
1022 gThisModule = PyModule_Create(&moduledef);
1023 if (!gThisModule)
1024 return nullptr;
1025
1026 // keep gThisModule, but do not increase its reference count even as it is
1027 // borrowed, or a self-referencing cycle would be created
1028
1029 // external types
1030 gPyTypeMap = PyDict_New();
1031 PyModule_AddObject(gThisModule, "type_map", gPyTypeMap); // steals reference
1032
1033 // Pythonizations ...
1034 PyModule_AddObject(gThisModule, "UserExceptions", PyDict_New());
1035
1036 // inject meta type
1037 if (!Utility::InitProxy(gThisModule, &CPPScope_Type, "CPPScope"))
1038 return nullptr;
1039
1040 // inject object proxy type
1041 if (!Utility::InitProxy(gThisModule, &CPPInstance_Type, "CPPInstance"))
1042 return nullptr;
1043
1044 // inject exception object proxy type
1045 if (!Utility::InitProxy(gThisModule, &CPPExcInstance_Type, "CPPExcInstance"))
1046 return nullptr;
1047
1048 // inject method proxy type
1049 if (!Utility::InitProxy(gThisModule, &CPPOverload_Type, "CPPOverload"))
1050 return nullptr;
1051
1052 // inject template proxy type
1053 if (!Utility::InitProxy(gThisModule, &TemplateProxy_Type, "TemplateProxy"))
1054 return nullptr;
1055
1056 // inject property proxy type
1057 if (!Utility::InitProxy(gThisModule, &CPPDataMember_Type, "CPPDataMember"))
1058 return nullptr;
1059
1061 "InstanceMethod"))
1062 return nullptr;
1063
1064 if (!Utility::InitProxy(gThisModule, &TupleOfInstances_Type, "InstanceArray"))
1065 return nullptr;
1066
1067 if (!Utility::InitProxy(gThisModule, &LowLevelView_Type, "LowLevelView"))
1068 return nullptr;
1069
1070 if (!Utility::InitProxy(gThisModule, &PyNullPtr_t_Type, "nullptr_t"))
1071 return nullptr;
1072
1073 // custom iterators
1074 if (PyType_Ready(&InstanceArrayIter_Type) < 0)
1075 return nullptr;
1076
1077 if (PyType_Ready(&IndexIter_Type) < 0)
1078 return nullptr;
1079
1080 if (PyType_Ready(&VectorIter_Type) < 0)
1081 return nullptr;
1082
1083 // inject identifiable nullptr and default
1084 gNullPtrObject = (PyObject*)&_cpyrt_NullPtrStruct;
1085 Py_INCREF(gNullPtrObject);
1086 PyModule_AddObject(gThisModule, (char*)"nullptr", gNullPtrObject);
1087
1088 gDefaultObject = (PyObject*)&_cpyrt_DefaultStruct;
1089 Py_INCREF(gDefaultObject);
1090 PyModule_AddObject(gThisModule, (char*)"default", gDefaultObject);
1091
1092 // C++-specific exceptions
1093 PyObject* cppfatal =
1094 PyErr_NewException((char*)"cppjit.ll.FatalError", nullptr, nullptr);
1095 PyModule_AddObject(gThisModule, (char*)"FatalError", cppfatal);
1096
1098 PyErr_NewException((char*)"cppjit.ll.BusError", cppfatal, nullptr);
1099 PyModule_AddObject(gThisModule, (char*)"BusError", gBusException);
1100 gSegvException = PyErr_NewException((char*)"cppjit.ll.SegmentationViolation",
1101 cppfatal, nullptr);
1102 PyModule_AddObject(gThisModule, (char*)"SegmentationViolation",
1104 gIllException = PyErr_NewException((char*)"cppjit.ll.IllegalInstruction",
1105 cppfatal, nullptr);
1106 PyModule_AddObject(gThisModule, (char*)"IllegalInstruction", gIllException);
1108 PyErr_NewException((char*)"cppjit.ll.AbortSignal", cppfatal, nullptr);
1109 PyModule_AddObject(gThisModule, (char*)"AbortSignal", gAbrtException);
1110
1111 // gbl namespace is injected in cppjit.py
1112
1113 // create the memory regulator
1114 static MemoryRegulator s_memory_regulator;
1115
1116 Py_INCREF(gThisModule);
1117 return gThisModule;
1118}
1119
1120} // namespace cppjit::cpyrt
_object PyObject
#define d(i)
Definition RSha256.hxx:102
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
void * GetAddress(CPPInstance *pyobj)
interop::TCppScope_t ObjectIsA(bool check_smart=true) const
virtual PyObject * FromMemory(void *address)
static PyObject * default_repr(PyObject *)
#define DEFINE_CALL_POLICY_TOGGLE(name, flagname)
static PyObject * nullptr_repr(PyObject *)
static PyNumberMethods nullptr_as_number
static struct PyModuleDef moduledef
static void default_dealloc(PyObject *)
#define GETSTATE(m)
static PyObject * UnlockInterpreter(PyObject *, PyObject *)
static PyTypeObject PyDefault_t_Type
static void nullptr_dealloc(PyObject *)
static int cpycppjitmodule_clear(PyObject *m)
static int nullptr_nonzero(PyObject *)
static PyTypeObject PyNullPtr_t_Type
static PyMethodDef gcpyrtMethods[]
static int cpycppjitmodule_traverse(PyObject *m, visitproc visit, void *arg)
std::unordered_map< interop::TCppType_t, interop::TCppType_t > TypeReductionMap
static PyObject * LockInterpreter(PyObject *, PyObject *)
#define Py_RETURN_TRUE
Definition cpyrt.h:73
#define Py_RETURN_FALSE
Definition cpyrt.h:77
#define PY_SSIZE_T_FORMAT
Definition cpyrt.h:65
#define CPYRT_PYTYPE_TAIL
Definition cpyrt.h:159
#define Py_RETURN_NONE
Definition cpyrt.h:69
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string compound(const std::string &name)
std::vector< Cpp::TemplateArgInfo > GetTemplateArgsTypes(PyObject *scope, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition Utility.cxx:892
bool InitProxy(PyObject *module, PyTypeObject *pytype, const char *name)
Definition Utility.cxx:1168
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:1204
CPYRT_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyObject * gPyTypeMap
PyObject * gSegvException
PyTypeObject CPPDataMember_Type
PyObject * gThisModule
Definition CPPMethod.cxx:30
PyTypeObject CPPScope_Type
Definition CPPScope.cxx:686
PyObject * gDefaultObject
PyObject * gNullPtrObject
bool LowLevelView_CheckExact(T *object)
PyTypeObject CPPInstance_Type
std::ostringstream gCapturedError
PyObject * GetScopeProxy(interop::TCppScope_t)
PyTypeObject CPPOverload_Type
PyTypeObject LowLevelView_Type
PyObject * BindCppObject(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
bool CPPOverload_CheckExact(T *object)
Definition CPPOverload.h:98
Py_ssize_t dim_t
Definition API.h:100
CPYRT_EXTERN void DestroyConverter(Converter *p)
std::unordered_set< interop::TCppScope_t > gPinnedTypes
PyTypeObject TemplateProxy_Type
PyTypeObject TupleOfInstances_Type
Representation of C-style array of instances.
PyTypeObject InstanceArrayIter_Type
PyTypeObject CustomInstanceMethod_Type
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
bool CreatePyStrings()
Definition PyStrings.cxx:88
PyObject * gAbrtException
bool CPPDataMember_Check(T *object)
PyTypeObject CPPExcInstance_Type
PyObject * BindCppObjectNoCast(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
std::streambuf * gOldErrorBuffer
CPYRT_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:105
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
bool CPPInstance_Check(T *object)
PyTypeObject VectorIter_Type
PyObject * Instance_FromVoidPtr(void *addr, const char *classname, int python_owns)
Definition API.cxx:161
PyTypeObject IndexIter_Type
PyObject * DestroyPyStrings()
PyObject * gBusException
std::unordered_map< std::string, std::vector< PyObject * > > & pythonizations()
PyObject * gIllException
PyObject * Init()
Cpp::TypeRef TCppType_t
Definition API.h:42
Cpp::ObjectRef TCppObject_t
Definition API.h:43
TCppType_t GetTypeFromScope(TCppScope_t klass)
std::string ResolveName(const std::string &cppitem_name)
bool IsSubclass(TCppScope_t derived, TCppScope_t base)
size_t SizeOf(TCppScope_t klass)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
ptrdiff_t GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror=false)
void * TCppFuncAddr_t
Definition API.h:46
TCppType_t GetType(const std::string &name, bool enable_slow_lookup=false)
bool IsAggregate(TCppScope_t type)
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
TCppScope_t InstantiateTemplate(TCppScope_t tmpl, Cpp::TemplateArgInfo *args, size_t args_size)
Definition API.cxx:34
PyObject_HEAD char * b_ptr
PyObject * error
TMarker m
Definition textangle.C:8