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 "Utility.h"
20#include "cppjit_interop.h"
21#include <unordered_map>
22
23#define CPYRT_INTERNAL 1
24#include "cpyrt/DispatchPtr.h"
25namespace cppjit::cpyrt {
26void* Instance_AsVoidPtr(PyObject* pyobject);
27PyObject* Instance_FromVoidPtr(void* addr, const std::string& classname,
28 bool python_owns = false);
29} // namespace cppjit::cpyrt
30#undef CPYRT_INTERNAL
31
32// Standard
33#include <algorithm>
34#include <iostream>
35#include <map>
36#include <sstream>
37#include <string>
38#include <unordered_set>
39#include <utility>
40#include <vector>
41
42std::unordered_map<interop::TCppType_t, interop::TCppType_t> TypeReductionMap;
43
44//- data -----------------------------------------------------------------------
46 return cpyrt_PyText_FromString("nullptr");
47}
48
50 Py_FatalError("deallocating nullptr");
51}
52
53static int nullptr_nonzero(PyObject*) { return 0; }
54
55static PyNumberMethods nullptr_as_number = {
56 0,
57 0,
58 0,
59 0,
60 0,
61 0,
62 0,
63 0,
64 0,
65 (inquiry)nullptr_nonzero, // tp_nonzero (nb_bool in p3)
66 0,
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, // nb_floor_divide
86 0, // nb_true_divide
87 0,
88 0,
89 0, // nb_index
90 0, // nb_matrix_multiply
91 0 // nb_inplace_matrix_multiply
92};
93
94static PyTypeObject PyNullPtr_t_Type = {
95 PyVarObject_HEAD_INIT(&PyType_Type, 0) "nullptr_t", // tp_name
96 sizeof(PyObject), // tp_basicsize
97 0, // tp_itemsize
98 nullptr_dealloc, // tp_dealloc (never called)
99 0,
100 0,
101 0,
102 0,
103 nullptr_repr, // tp_repr
104 &nullptr_as_number, // tp_as_number
105 0,
106 0,
107#if PY_VERSION_HEX >= 0x030d0000
108 (hashfunc)Py_HashPointer, // tp_hash
109#else
110 (hashfunc)_Py_HashPointer, // tp_hash
111#endif
112 0,
113 0,
114 0,
115 0,
116 0,
117 Py_TPFLAGS_DEFAULT,
118 0,
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, // tp_del
144 0, // tp_version_tag
145 0, // tp_finalize
146 0 // tp_vectorcall
148
150 return cpyrt_PyText_FromString("type default");
151}
152
154 Py_FatalError("deallocating default");
155}
156
157static PyTypeObject PyDefault_t_Type = {
158 PyVarObject_HEAD_INIT(&PyType_Type, 0) "default_t", // tp_name
159 sizeof(PyObject), // tp_basicsize
160 0, // tp_itemsize
161 default_dealloc, // tp_dealloc (never called)
162 0,
163 0,
164 0,
165 0,
166 default_repr, // tp_repr
167 0,
168 0,
169 0,
170#if PY_VERSION_HEX >= 0x030d0000
171 (hashfunc)Py_HashPointer, // tp_hash
172#else
173 (hashfunc)_Py_HashPointer, // tp_hash
174#endif
175 0,
176 0,
177 0,
178 0,
179 0,
180 Py_TPFLAGS_DEFAULT,
181 0,
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, // tp_del
207 0, // tp_version_tag
208 0, // tp_finalize
209 0 // tp_vectorcall
211
212namespace {
213
214struct {
215 PyObject_HEAD
216} _cpyrt_NullPtrStruct{PyObject_HEAD_INIT(&PyNullPtr_t_Type)};
217
218struct {
219 PyObject_HEAD
220} _cpyrt_DefaultStruct{PyObject_HEAD_INIT(&PyDefault_t_Type)};
221
222// TODO: refactor with Converters.cxx
223struct cpyrt_tagCDataObject { // non-public (but stable)
224 PyObject_HEAD char* b_ptr;
225 int b_needsfree;
226};
227
228} // unnamed namespace
229
230namespace cppjit::cpyrt {
231PyObject* gThisModule = nullptr;
233PyObject* gNullPtrObject = nullptr;
239std::unordered_set<interop::TCppScope_t> gPinnedTypes;
240std::ostringstream gCapturedError;
241std::streambuf* gOldErrorBuffer = nullptr;
242
243std::unordered_map<std::string, std::vector<PyObject*>>& pythonizations() {
244 static std::unordered_map<std::string, std::vector<PyObject*>> pyzMap;
245 return pyzMap;
246}
247} // namespace cppjit::cpyrt
248
249//- private helpers ------------------------------------------------------------
250namespace {
251
252using namespace cppjit::cpyrt;
253
254//----------------------------------------------------------------------------
255static PyObject* MakeCppTemplateClass(PyObject* /* self */, PyObject* args) {
256 // Create a binding for a templated class instantiation.
257
258 // args is class name + template arguments; build full instantiation
259 Py_ssize_t nArgs = PyTuple_GET_SIZE(args);
260 if (nArgs < 2) {
261 PyErr_Format(PyExc_TypeError,
262 "too few arguments for template instantiation");
263 return nullptr;
264 }
265 PyObject* cppscope = PyTuple_GET_ITEM(args, 0);
266 void* tmpl = PyLong_AsVoidPtr(cppscope);
267
268 // build "< type, type, ... >" part of class name (modifies pyname)
269 std::vector<Cpp::TemplateArgInfo> types =
270 Utility::GetTemplateArgsTypes(cppscope, args, nullptr, Utility::kNone, 1);
271 if (PyErr_Occurred())
272 return nullptr;
273
275 interop::InstantiateTemplate(tmpl, types.data(), types.size());
276 for (Cpp::TemplateArgInfo i : types) {
277 if (i.m_IntegralValue)
278 std::free((void*)i.m_IntegralValue);
279 }
280
281 if (!scope) {
282 PyErr_Format(PyExc_TypeError,
283 "Template instantiation failed: '%s' with args: '%s\n'",
284 interop::GetScopedFinalName(tmpl).c_str(),
285 cpyrt_PyText_AsString(PyObject_Repr(args)));
286 return nullptr;
287 }
288
289 return CreateScopeProxy(scope);
290}
291
292//----------------------------------------------------------------------------
293static char* GCIA_kwlist[] = {(char*)"instance", (char*)"field", (char*)"byref",
294 NULL};
295static void* GetCPPInstanceAddress(const char* fname, PyObject* args,
296 PyObject* kwds) {
297 // Helper to get the address (address-of-address) of various object proxy
298 // types.
299 CPPInstance* pyobj = 0;
300 PyObject* pyname = 0;
301 int byref = 0;
302 if (PyArg_ParseTupleAndKeywords(args, kwds, const_cast<char*>("O|O!b"),
303 GCIA_kwlist, &pyobj, &cpyrt_PyText_Type,
304 &pyname, &byref)) {
305
306 if (CPPInstance_Check(pyobj)) {
307 if (pyname != 0) {
308 // locate property proxy for offset info
309 CPPDataMember* pyprop = nullptr;
310
311 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)pyobj);
312 PyObject* dict = PyObject_GetAttr(pyclass, PyStrings::gDict);
313 pyprop = (CPPDataMember*)PyObject_GetItem(dict, pyname);
314 Py_DECREF(dict);
315
316 if (CPPDataMember_Check(pyprop)) {
317 // this is an address of a value (i.e. &myobj->prop)
318 void* addr = (void*)pyprop->GetAddress(pyobj);
319 Py_DECREF(pyprop);
320 return addr;
321 }
322
323 Py_XDECREF(pyprop);
324
325 PyErr_Format(PyExc_TypeError, "%s is not a valid data member",
326 cpyrt_PyText_AsString(pyname));
327 return nullptr;
328 }
329
330 // this is an address of an address (i.e. &myobj, with myobj of type
331 // MyObj*) note that the return result may be null
332 if (!byref)
333 return ((CPPInstance*)pyobj)->GetObject();
334 return &((CPPInstance*)pyobj)->GetObjectRaw();
335
336 } else if (cpyrt_PyText_Check(pyobj)) {
337 // special cases for access to the cpyrt API
338 std::string req = cpyrt_PyText_AsString((PyObject*)pyobj);
339 if (req == "Instance_AsVoidPtr")
340 return (void*)&Instance_AsVoidPtr;
341 else if (req == "Instance_FromVoidPtr")
342 return (void*)&Instance_FromVoidPtr;
343 }
344 }
345
346 if (!PyErr_Occurred())
347 PyErr_Format(PyExc_ValueError, "invalid argument for %s", fname);
348 return nullptr;
349}
350
351//----------------------------------------------------------------------------
352static PyObject* addressof(PyObject* /* dummy */, PyObject* args,
353 PyObject* kwds) {
354 // Return object proxy address as a value (cppjit-style), or the same for an
355 // array.
356 void* addr = GetCPPInstanceAddress("addressof", args, kwds);
357 if (addr)
358 return PyLong_FromLongLong((intptr_t)addr);
359 else if (!PyErr_Occurred()) {
360 return PyLong_FromLong(0);
361 } else if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args) == 1) {
362 PyErr_Clear();
363 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
364
365 // nullptr special case
366 if (arg0 == gNullPtrObject ||
367 (PyInt_Check(arg0) && PyInt_AsLong(arg0) == 0))
368 return PyLong_FromLong(0);
369
370 // overload if unambiguous
371 if (CPPOverload_CheckExact(arg0)) {
372 const auto& methods = ((CPPOverload*)arg0)->fMethodInfo->fMethods;
373 if (methods.size() != 1) {
374 PyErr_SetString(PyExc_TypeError, "overload is not unambiguous");
375 return nullptr;
376 }
377
378 interop::TCppFuncAddr_t caddr = methods[0]->GetFunctionAddress();
379 return PyLong_FromLongLong((intptr_t)caddr);
380 }
381
382 // C functions (incl. ourselves)
383 if (PyCFunction_Check(arg0)) {
384 void* caddr = (void*)PyCFunction_GetFunction(arg0);
385 return PyLong_FromLongLong((intptr_t)caddr);
386 }
387
388 // LowLevelViews
389 if (LowLevelView_CheckExact(arg0)) {
390 auto* llv = (LowLevelView*)arg0;
391 return PyLong_FromLongLong((intptr_t)llv->get_buf());
392 }
393
394 // final attempt: any type of buffer
395 Utility::GetBuffer(arg0, '*', 1, addr, false);
396 if (addr)
397 return PyLong_FromLongLong((intptr_t)addr);
398 }
399
400 // error message if not already set
401 if (!PyErr_Occurred()) {
402 if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args)) {
403 PyObject* str = PyObject_Str(PyTuple_GET_ITEM(args, 0));
404 if (str && cpyrt_PyText_Check(str))
405 PyErr_Format(PyExc_TypeError, "unknown object %s",
407 else
408 PyErr_Format(PyExc_TypeError, "unknown object at %p",
409 (void*)PyTuple_GET_ITEM(args, 0));
410 Py_XDECREF(str);
411 }
412 }
413 return nullptr;
414}
415
416//----------------------------------------------------------------------------
417static PyObject* AsCObject(PyObject* /* unused */, PyObject* args,
418 PyObject* kwds) {
419 // Return object proxy as an opaque CObject.
420 void* addr = GetCPPInstanceAddress("as_cobject", args, kwds);
421 if (addr)
422 return cpyrt_PyCapsule_New((void*)addr, nullptr, nullptr);
423 return nullptr;
424}
425
426//----------------------------------------------------------------------------
427static PyObject* AsCapsule(PyObject* /* unused */, PyObject* args,
428 PyObject* kwds) {
429 // Return object proxy as an opaque PyCapsule.
430 void* addr = GetCPPInstanceAddress("as_capsule", args, kwds);
431 if (addr)
432 return PyCapsule_New(addr, nullptr, nullptr);
433 return nullptr;
434}
435
436//----------------------------------------------------------------------------
437static PyObject* AsCTypes(PyObject* /* unused */, PyObject* args,
438 PyObject* kwds) {
439 // Return object proxy as a ctypes c_void_p
440 void* addr = GetCPPInstanceAddress("as_ctypes", args, kwds);
441 if (!addr)
442 return nullptr;
443
444 // TODO: refactor code below with converters code
445 static PyTypeObject* ct_cvoidp = nullptr;
446 if (!ct_cvoidp) {
447 PyObject* ctmod = PyImport_ImportModule("ctypes"); // ref-count kept
448 if (!ctmod)
449 return nullptr;
450
451 ct_cvoidp = (PyTypeObject*)PyObject_GetAttrString(ctmod, "c_void_p");
452 Py_DECREF(ctmod);
453 if (!ct_cvoidp)
454 return nullptr;
455 Py_DECREF(ct_cvoidp); // module keeps a reference
456 }
457
458 PyObject* ref = ct_cvoidp->tp_new(ct_cvoidp, nullptr, nullptr);
459 *(void**)((cpyrt_tagCDataObject*)ref)->b_ptr = addr;
460 ((cpyrt_tagCDataObject*)ref)->b_needsfree = 0;
461 return ref;
462}
463
464//----------------------------------------------------------------------------
465static PyObject* AsMemoryView(PyObject* /* unused */, PyObject* pyobject) {
466 // Return a raw memory view on arrays of PODs.
467 if (!CPPInstance_Check(pyobject)) {
468 PyErr_SetString(PyExc_TypeError, "C++ object proxy expected");
469 return nullptr;
470 }
471
472 CPPInstance* pyobj = (CPPInstance*)pyobject;
473 interop::TCppScope_t klass = ((CPPClass*)Py_TYPE(pyobject))->fCppType;
474
475 Py_ssize_t array_len = pyobj->ArrayLength();
476
477 if (array_len < 0 || !interop::IsAggregate(klass)) {
478 PyErr_SetString(PyExc_TypeError,
479 "object is not a proxy to an array of PODs of known size");
480 return nullptr;
481 }
482
483 Py_buffer view;
484
485 view.obj = pyobject;
486 view.buf = pyobj->GetObject();
487 view.itemsize = interop::SizeOf(klass);
488 view.len = view.itemsize * array_len;
489 view.readonly = 0;
490 view.format = NULL; // i.e. "B" assumed
491 view.ndim = 1;
492 view.shape = NULL;
493 view.strides = NULL;
494 view.suboffsets = NULL;
495 view.internal = NULL;
496
497 return PyMemoryView_FromBuffer(&view);
498}
499
500//----------------------------------------------------------------------------
501static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) {
502 // From a long representing an address or a PyCapsule/CObject, bind to a
503 // class.
504 Py_ssize_t argc = PyTuple_GET_SIZE(args);
505 if (argc != 2) {
506 PyErr_Format(
507 PyExc_TypeError,
508 "bind_object takes 2 positional arguments but (" PY_SSIZE_T_FORMAT
509 " were given)",
510 argc);
511 return nullptr;
512 }
513
514 // convert 2nd argument first (used for both pointer value and instance cases)
515 interop::TCppScope_t cast_type = nullptr;
516 PyObject* arg1 = PyTuple_GET_ITEM(args, 1);
517 if (!cpyrt_PyText_Check(arg1)) { // not string, then class
518 if (CPPScope_Check(arg1))
519 cast_type = ((CPPClass*)arg1)->fCppType;
520 else
521 arg1 = PyObject_GetAttr(arg1, PyStrings::gName);
522 } else
523 Py_INCREF(arg1);
524
525 if (!cast_type && arg1) {
526 cast_type =
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 = cpyrt_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* Move(PyObject*, PyObject* pyobject) {
671 // Prepare the given C++ object for moving.
672 if (!CPPInstance_Check(pyobject)) {
673 PyErr_SetString(PyExc_TypeError, "C++ object expected");
674 return nullptr;
675 }
676
677 ((CPPInstance*)pyobject)->fFlags |= CPPInstance::kIsRValue;
678 Py_INCREF(pyobject);
679 return pyobject;
680}
681
682//----------------------------------------------------------------------------
683static PyObject* AddPythonization(PyObject*, PyObject* args) {
684 // Remove a previously registered pythonizor from the given scope.
685 PyObject* pythonizor = nullptr;
686 const char* scope;
687 if (!PyArg_ParseTuple(args, const_cast<char*>("Os"), &pythonizor, &scope))
688 return nullptr;
689
690 if (!PyCallable_Check(pythonizor)) {
691 PyObject* pystr = PyObject_Str(pythonizor);
692 PyErr_Format(PyExc_TypeError, "given \'%s\' object is not callable",
693 cpyrt_PyText_AsString(pystr));
694 Py_DECREF(pystr);
695 return nullptr;
696 }
697
698 Py_INCREF(pythonizor);
699 pythonizations()[scope].push_back(pythonizor);
700
702}
703
704//----------------------------------------------------------------------------
705static PyObject* RemovePythonization(PyObject*, PyObject* args) {
706 // Remove a previously registered pythonizor from the given scope.
707 PyObject* pythonizor = nullptr;
708 const char* scope;
709 if (!PyArg_ParseTuple(args, const_cast<char*>("Os"), &pythonizor, &scope))
710 return nullptr;
711
712 auto& pyzMap = pythonizations();
713 auto p1 = pyzMap.find(scope);
714 if (p1 != pyzMap.end()) {
715 auto p2 = std::find(p1->second.begin(), p1->second.end(), pythonizor);
716 if (p2 != p1->second.end()) {
717 p1->second.erase(p2);
719 }
720 }
721
723}
724
725//----------------------------------------------------------------------------
726static PyObject* PinType(PyObject*, PyObject* pyclass) {
727 // Add a pinning so that objects of type `derived' are interpreted as
728 // objects of type `base'.
729 if (!CPPScope_Check(pyclass)) {
730 PyErr_SetString(PyExc_TypeError, "C++ class expected");
731 return nullptr;
732 }
733
734 gPinnedTypes.insert(((CPPClass*)pyclass)->fCppType);
735
737}
738
739//----------------------------------------------------------------------------
740static PyObject* AddTypeReducer(PyObject*, PyObject* args) {
741 // Add a type reducer to map type2 to type2 on function returns.
742 const char *reducable, *reduced;
743 if (!PyArg_ParseTuple(args, const_cast<char*>("ss"), &reducable, &reduced))
744 return nullptr;
745
746 interop::TCppType_t reducable_type =
748 interop::TCppType_t reduced_type =
750 TypeReductionMap[reducable_type] = reduced_type;
751
753}
754
755#define DEFINE_CALL_POLICY_TOGGLE(name, flagname) \
756 static PyObject* name(PyObject*, PyObject* args) { \
757 PyObject* enabled = 0; \
758 if (!PyArg_ParseTuple(args, const_cast<char*>("O"), &enabled)) \
759 return nullptr; \
760 \
761 if (CallContext::SetGlobalPolicy(CallContext::flagname, \
762 PyObject_IsTrue(enabled))) { \
763 Py_RETURN_TRUE; \
764 } \
765 \
766 Py_RETURN_FALSE; \
767 }
768
769DEFINE_CALL_POLICY_TOGGLE(SetHeuristicMemoryPolicy, kUseHeuristics);
770DEFINE_CALL_POLICY_TOGGLE(SetImplicitSmartPointerConversion,
771 kImplicitSmartPtrConversion);
772DEFINE_CALL_POLICY_TOGGLE(SetGlobalSignalPolicy, kProtected);
773
774//----------------------------------------------------------------------------
775static PyObject* SetOwnership(PyObject*, PyObject* args) {
776 // Set the ownership (True is python-owns) for the given object.
777 CPPInstance* pyobj = nullptr;
778 PyObject* pykeep = nullptr;
779 if (!PyArg_ParseTuple(args, const_cast<char*>("O!O!"), &CPPInstance_Type,
780 (void*)&pyobj, &PyInt_Type, &pykeep))
781 return nullptr;
782
783 (bool)PyLong_AsLong(pykeep) ? pyobj->PythonOwns() : pyobj->CppOwns();
784
786}
787
788//----------------------------------------------------------------------------
789static PyObject* AddSmartPtrType(PyObject*, PyObject* args) {
790 // Add a smart pointer to the list of known smart pointer types.
791 const char* type_name;
792 if (!PyArg_ParseTuple(args, const_cast<char*>("s"), &type_name))
793 return nullptr;
794
795 // interop::AddSmartPtrType(type_name);
796
798}
799
800//----------------------------------------------------------------------------
801static PyObject* BeginCaptureStderr(PyObject*, PyObject*) {
802 gOldErrorBuffer = std::cerr.rdbuf();
803 std::cerr.rdbuf(gCapturedError.rdbuf());
804
806}
807
808//----------------------------------------------------------------------------
809static PyObject* EndCaptureStderr(PyObject*, PyObject*) {
810 // restore old rdbuf and return captured result
811 std::cerr.rdbuf(gOldErrorBuffer);
812 gOldErrorBuffer = nullptr;
813
814 std::string capturedError = std::move(gCapturedError).str();
815
816 gCapturedError.str("");
817 gCapturedError.clear();
818
819 return Py_BuildValue("s", capturedError.c_str());
820}
821} // unnamed namespace
822
823//- data -----------------------------------------------------------------------
824//----------------------------------------------------------------------------
826 // release the GIL while waiting: the thread holding the interpreter lock
827 // may need Python to finish
828 Py_BEGIN_ALLOW_THREADS
830 Py_END_ALLOW_THREADS
832}
833
834//----------------------------------------------------------------------------
839
840static PyMethodDef gcpyrtMethods[] = {
841 {(char*)"CreateScopeProxy", (PyCFunction)cpyrt::CreateScopeProxy,
842 METH_VARARGS, (char*)"cppjit internal function"},
843 {(char*)"MakeCppTemplateClass", (PyCFunction)MakeCppTemplateClass,
844 METH_VARARGS, (char*)"cppjit internal function"},
845 {(char*)"_DestroyPyStrings", (PyCFunction)cpyrt::DestroyPyStrings,
846 METH_NOARGS, (char*)"cppjit internal function"},
847 {(char*)"addressof", (PyCFunction)addressof, METH_VARARGS | METH_KEYWORDS,
848 (char*)"Retrieve address of proxied object or field as a value."},
849 {(char*)"as_cobject", (PyCFunction)AsCObject, METH_VARARGS | METH_KEYWORDS,
850 (char*)"Retrieve address of proxied object or field in a CObject."},
851 {(char*)"as_capsule", (PyCFunction)AsCapsule, METH_VARARGS | METH_KEYWORDS,
852 (char*)"Retrieve address of proxied object or field in a PyCapsule."},
853 {(char*)"as_ctypes", (PyCFunction)AsCTypes, METH_VARARGS | METH_KEYWORDS,
854 (char*)"Retrieve address of proxied object or field in a ctypes "
855 "c_void_p."},
856 {(char*)"as_memoryview", (PyCFunction)AsMemoryView, METH_O,
857 (char*)"Represent an array of objects as raw memory."},
858 {(char*)"bind_object", (PyCFunction)BindObject,
859 METH_VARARGS | METH_KEYWORDS,
860 (char*)"Create an object of given type, from given address."},
861 {(char*)"move", (PyCFunction)Move, METH_O,
862 (char*)"Cast the C++ object to become movable."},
863 {(char*)"add_pythonization", (PyCFunction)AddPythonization, METH_VARARGS,
864 (char*)"Add a pythonizor."},
865 {(char*)"remove_pythonization", (PyCFunction)RemovePythonization,
866 METH_VARARGS, (char*)"Remove a pythonizor."},
867 {(char*)"_pin_type", (PyCFunction)PinType, METH_O,
868 (char*)"Install a type pinning."},
869 {(char*)"_add_type_reducer", (PyCFunction)AddTypeReducer, METH_VARARGS,
870 (char*)"Add a type reducer."},
871 {(char*)"SetHeuristicMemoryPolicy", (PyCFunction)SetHeuristicMemoryPolicy,
872 METH_VARARGS,
873 (char*)"Set the global memory policy, which affects object ownership when "
874 "objects are passed as function arguments."},
875 {(char*)"SetImplicitSmartPointerConversion",
876 (PyCFunction)SetImplicitSmartPointerConversion, METH_VARARGS,
877 (char*)"Enable or disable the implicit conversion to smart pointers in "
878 "function calls (on by default)."},
879 {(char*)"SetGlobalSignalPolicy", (PyCFunction)SetGlobalSignalPolicy,
880 METH_VARARGS,
881 (char*)"Set the global signal policy, which determines whether a jmp "
882 "address should be saved to return to after a "
883 "C++ segfault. In practical terms: trap signals in safe mode to "
884 "prevent interpreter abort."},
885 {(char*)"SetOwnership", (PyCFunction)SetOwnership, METH_VARARGS,
886 (char*)"Modify held C++ object ownership."},
887 {(char*)"AddSmartPtrType", (PyCFunction)AddSmartPtrType, METH_VARARGS,
888 (char*)"Add a smart pointer to the list of known smart pointer types."},
889 {(char*)"_begin_capture_stderr", (PyCFunction)BeginCaptureStderr,
890 METH_NOARGS, (char*)"Begin capturing stderr to a in memory buffer."},
891 {(char*)"_end_capture_stderr", (PyCFunction)EndCaptureStderr, METH_NOARGS,
892 (char*)"End capturing stderr and returns the captured buffer."},
893 {(char*)"_lock_interpreter", (PyCFunction)LockInterpreter, METH_NOARGS,
894 (char*)"Take the interpreter lock; pair with _unlock_interpreter."},
895 {(char*)"_unlock_interpreter", (PyCFunction)UnlockInterpreter,
896 METH_NOARGS, (char*)"Release the lock taken by _lock_interpreter."},
897 {nullptr, nullptr, 0, nullptr}};
898
902
903#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
904
905static int cpycppjitmodule_traverse(PyObject* m, visitproc visit, void* arg) {
906 Py_VISIT(GETSTATE(m)->error);
907 return 0;
908}
909
911 Py_CLEAR(GETSTATE(m)->error);
912 return 0;
913}
914
915static struct PyModuleDef moduledef = {
916 PyModuleDef_HEAD_INIT, "libcppjit", nullptr,
917 sizeof(struct module_state), gcpyrtMethods, nullptr,
919
920namespace cppjit::cpyrt {
921
922//----------------------------------------------------------------------------
924 // Initialization of extension module libcppjit.
925
926 // load commonly used python strings
928 return nullptr;
929
930 // setup this module
931 gThisModule = PyModule_Create(&moduledef);
932 if (!gThisModule)
933 return nullptr;
934
935 // keep gThisModule, but do not increase its reference count even as it is
936 // borrowed, or a self-referencing cycle would be created
937
938 // external types
939 gPyTypeMap = PyDict_New();
940 PyModule_AddObject(gThisModule, "type_map", gPyTypeMap); // steals reference
941
942 // Pythonizations ...
943 PyModule_AddObject(gThisModule, "UserExceptions", PyDict_New());
944
945 // inject meta type
946 if (!Utility::InitProxy(gThisModule, &CPPScope_Type, "CPPScope"))
947 return nullptr;
948
949 // inject object proxy type
950 if (!Utility::InitProxy(gThisModule, &CPPInstance_Type, "CPPInstance"))
951 return nullptr;
952
953 // inject exception object proxy type
954 if (!Utility::InitProxy(gThisModule, &CPPExcInstance_Type, "CPPExcInstance"))
955 return nullptr;
956
957 // inject method proxy type
958 if (!Utility::InitProxy(gThisModule, &CPPOverload_Type, "CPPOverload"))
959 return nullptr;
960
961 // inject template proxy type
962 if (!Utility::InitProxy(gThisModule, &TemplateProxy_Type, "TemplateProxy"))
963 return nullptr;
964
965 // inject property proxy type
966 if (!Utility::InitProxy(gThisModule, &CPPDataMember_Type, "CPPDataMember"))
967 return nullptr;
968
970 "InstanceMethod"))
971 return nullptr;
972
974 return nullptr;
975
976 if (!Utility::InitProxy(gThisModule, &LowLevelView_Type, "LowLevelView"))
977 return nullptr;
978
980 return nullptr;
981
982 // custom iterators
983 if (PyType_Ready(&InstanceArrayIter_Type) < 0)
984 return nullptr;
985
986 if (PyType_Ready(&IndexIter_Type) < 0)
987 return nullptr;
988
989 if (PyType_Ready(&VectorIter_Type) < 0)
990 return nullptr;
991
992 // inject identifiable nullptr and default
993 gNullPtrObject = (PyObject*)&_cpyrt_NullPtrStruct;
994 Py_INCREF(gNullPtrObject);
995 PyModule_AddObject(gThisModule, (char*)"nullptr", gNullPtrObject);
996
997 gDefaultObject = (PyObject*)&_cpyrt_DefaultStruct;
998 Py_INCREF(gDefaultObject);
999 PyModule_AddObject(gThisModule, (char*)"default", gDefaultObject);
1000
1001 // C++-specific exceptions
1002 PyObject* cppfatal =
1003 PyErr_NewException((char*)"cppjit.ll.FatalError", nullptr, nullptr);
1004 PyModule_AddObject(gThisModule, (char*)"FatalError", cppfatal);
1005
1007 PyErr_NewException((char*)"cppjit.ll.BusError", cppfatal, nullptr);
1008 PyModule_AddObject(gThisModule, (char*)"BusError", gBusException);
1009 gSegvException = PyErr_NewException((char*)"cppjit.ll.SegmentationViolation",
1010 cppfatal, nullptr);
1011 PyModule_AddObject(gThisModule, (char*)"SegmentationViolation",
1013 gIllException = PyErr_NewException((char*)"cppjit.ll.IllegalInstruction",
1014 cppfatal, nullptr);
1015 PyModule_AddObject(gThisModule, (char*)"IllegalInstruction", gIllException);
1017 PyErr_NewException((char*)"cppjit.ll.AbortSignal", cppfatal, nullptr);
1018 PyModule_AddObject(gThisModule, (char*)"AbortSignal", gAbrtException);
1019
1020 // gbl namespace is injected in cppjit.py
1021
1022 // create the memory regulator
1023 static MemoryRegulator s_memory_regulator;
1024
1025 Py_INCREF(gThisModule);
1026 return gThisModule;
1027}
1028
1029} // namespace cppjit::cpyrt
_object PyObject
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
void * GetAddress(CPPInstance *pyobj)
interop::TCppScope_t ObjectIsA(bool check_smart=true) const
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:126
#define Py_RETURN_FALSE
Definition cpyrt.h:130
#define cpyrt_PyCapsule_New
Definition cpyrt.h:86
#define PY_SSIZE_T_FORMAT
Definition cpyrt.h:118
#define PyInt_AsLong
Definition cpyrt.h:77
#define cpyrt_PyText_Type
Definition cpyrt.h:73
#define cpyrt_PyText_Check
Definition cpyrt.h:47
#define CPYRT_PYTYPE_TAIL
Definition cpyrt.h:215
#define PyInt_Type
Definition cpyrt.h:84
#define cpyrt_PyCapsule_GetPointer
Definition cpyrt.h:88
#define cpyrt_PyText_AsString
Definition cpyrt.h:49
#define cpyrt_PyText_FromString
Definition cpyrt.h:55
#define Py_RETURN_NONE
Definition cpyrt.h:122
#define PyInt_Check
Definition cpyrt.h:76
std::vector< Cpp::TemplateArgInfo > GetTemplateArgsTypes(PyObject *scope, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition Utility.cxx:893
bool InitProxy(PyObject *module, PyTypeObject *pytype, const char *name)
Definition Utility.cxx:1169
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:1205
PyObject * gPyTypeMap
PyObject * gSegvException
PyTypeObject CPPDataMember_Type
PyObject * gThisModule
Definition CPPMethod.cxx:30
PyTypeObject CPPScope_Type
Definition CPPScope.cxx:687
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
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)
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
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