File: | build/../torch/csrc/fx/fx_init.cpp |
Warning: | line 133, column 21 PyObject ownership leak with reference count of 1 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | #include <torch/csrc/utils/pybind.h> | |||
2 | ||||
3 | namespace torch { | |||
4 | namespace fx { | |||
5 | ||||
6 | struct ToRestore { | |||
7 | PyObject* m_self; | |||
8 | PyMethodDef* m_ml; | |||
9 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | |||
10 | vectorcallfunc vectorcall; | |||
11 | #endif | |||
12 | PyObject* original_fn; // The original method we are trying to patch | |||
13 | PyObject* patch_fn; // The function we're patching in place of original_fn | |||
14 | }; | |||
15 | ||||
16 | class DecRefGuard { | |||
17 | public: | |||
18 | DecRefGuard(PyObject* obj) : obj(obj) {} | |||
19 | ~DecRefGuard() { | |||
20 | Py_DECREF(obj)_Py_DECREF(((PyObject*)(obj))); | |||
21 | } | |||
22 | ||||
23 | private: | |||
24 | PyObject* obj; | |||
25 | }; | |||
26 | ||||
27 | PyObject* replacement_method(PyObject* self, PyObject* args, PyObject* kwargs) { | |||
28 | DecRefGuard self_guard(self); | |||
29 | // restore the implementation immediately so that patch_fn lives for as little | |||
30 | // as possible | |||
31 | ToRestore* to_restore = (ToRestore*)PyBytes_AsString(self); | |||
32 | PyCFunctionObject* patch_method_c = | |||
33 | ((PyCFunctionObject*)to_restore->original_fn); | |||
34 | patch_method_c->m_self = to_restore->m_self; | |||
35 | patch_method_c->m_ml = to_restore->m_ml; | |||
36 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | |||
37 | patch_method_c->vectorcall = to_restore->vectorcall; | |||
38 | #endif | |||
39 | ||||
40 | if (kwargs) { | |||
41 | Py_INCREF(kwargs)_Py_INCREF(((PyObject*)(kwargs))); | |||
42 | } else { | |||
43 | kwargs = PyDict_New(); | |||
44 | } | |||
45 | DecRefGuard kwargs_guard(kwargs); | |||
46 | ||||
47 | PyObject* result = nullptr; | |||
48 | // Creates a tuple of 3 python objects | |||
49 | PyObject* args_ = | |||
50 | Py_BuildValue("(OOO)", to_restore->original_fn, args, kwargs); | |||
51 | if (!args_) { | |||
52 | return nullptr; | |||
53 | } | |||
54 | DecRefGuard args_guard(args_); | |||
55 | // Calls the patched function with arguments of (original function, args, | |||
56 | // kwargs) | |||
57 | result = PyEval_CallObject(to_restore->patch_fn, args_)PyEval_CallObjectWithKeywords(to_restore->patch_fn, args_, (PyObject *)__null); | |||
58 | return result; | |||
59 | } | |||
60 | // The general idea is that we're patching a PyCFunctionObject, which has a | |||
61 | // couple relevant parts: m_ml: A PyMethodDef (the actual function to call) | |||
62 | // m_self: The self arg. | |||
63 | // vectorcall: An alternate calling convention (Python 3.8+) | |||
64 | // Usually we call obj.m_ml(obj.m_self, args, kwargs). However, we want to patch | |||
65 | // m_ml with ReplacementMethod (which calls our user-provided `patch_fn`). Thus, | |||
66 | // we also replace `m_self` with `ToRestore`, which contains all the information | |||
67 | // needed to restore the original function. | |||
68 | // | |||
69 | // `patch_function` parses the necessary information from the original | |||
70 | // PyCFunction and then patches it. When that function is called, it calls | |||
71 | // `replacement_method`, which then restores back the original `m_ml` and | |||
72 | // `m_self` values, as well as calling the user-defined `patch_fn`. | |||
73 | ||||
74 | static PyObject* patch_function(PyObject* self, PyObject* args) { | |||
75 | static PyMethodDef ReplacementMethod = { | |||
76 | "replace", | |||
77 | (PyCFunction)(void (*)())replacement_method, | |||
78 | METH_VARARGS0x0001 | METH_KEYWORDS0x0002, | |||
79 | "Replaced method implementation."}; | |||
80 | ||||
81 | ToRestore to_restore = {}; | |||
82 | if (!PyArg_ParseTuple( | |||
83 | args, "OO", &to_restore.original_fn, &to_restore.patch_fn)) { | |||
84 | return nullptr; | |||
85 | } | |||
86 | if (!PyCFunction_Check(to_restore.original_fn)((((PyObject*)(to_restore.original_fn))->ob_type) == & PyCFunction_Type)) { | |||
87 | std::stringstream err; | |||
88 | err << "Patched object "; | |||
89 | PyObject* obj_repr = PyObject_Repr(to_restore.original_fn); | |||
90 | if (PyUnicode_Check(obj_repr)((((((PyObject*)(obj_repr))->ob_type))->tp_flags & ( (1UL << 28))) != 0)) { | |||
91 | err << PyUnicode_AS_DATA(obj_repr)((const char *)(( (((PyASCIIObject *)(obj_repr))->wstr) ? ( ((PyASCIIObject *)(obj_repr))->wstr) : PyUnicode_AsUnicode (((PyObject*)(obj_repr)))))) << " "; | |||
92 | } | |||
93 | err << " is not a CFunction. Please report a bug to PyTorch!"; | |||
94 | PyErr_SetString(PyExc_RuntimeError, err.str().c_str()); | |||
95 | return nullptr; | |||
96 | } | |||
97 | DecRefGuard patch_fn_guard(to_restore.patch_fn); | |||
98 | Py_INCREF(to_restore.patch_fn)_Py_INCREF(((PyObject*)(to_restore.patch_fn))); | |||
99 | DecRefGuard patched_method_guard(to_restore.original_fn); | |||
100 | Py_INCREF(to_restore.original_fn)_Py_INCREF(((PyObject*)(to_restore.original_fn))); | |||
101 | PyCFunctionObject* patch_method_c = | |||
102 | ((PyCFunctionObject*)to_restore.original_fn); | |||
103 | ||||
104 | to_restore.m_self = patch_method_c->m_self; | |||
105 | to_restore.m_ml = patch_method_c->m_ml; | |||
106 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | |||
107 | to_restore.vectorcall = patch_method_c->vectorcall; | |||
108 | #endif | |||
109 | ||||
110 | patch_method_c->m_self = | |||
111 | PyBytes_FromStringAndSize((const char*)&to_restore, sizeof(ToRestore)); | |||
112 | patch_method_c->m_ml = &ReplacementMethod; | |||
113 | #if PY_VERSION_HEX((3 << 24) | (8 << 16) | (5 << 8) | (0xF << 4) | (0 << 0)) >= 0x03080000 | |||
114 | patch_method_c->vectorcall = nullptr; | |||
115 | #endif | |||
116 | return Py_None(&_Py_NoneStruct); | |||
117 | } | |||
118 | ||||
119 | ||||
120 | void initFx(PyObject* module) { | |||
121 | static std::array<PyMethodDef, 2> PatchMethods = {{ | |||
122 | {"patch_function", patch_function, METH_VARARGS0x0001, "Save"}, | |||
123 | {nullptr}, | |||
124 | }}; | |||
125 | ||||
126 | static struct PyModuleDef path = { | |||
127 | PyModuleDef_HEAD_INIT{ { 1, __null }, __null, 0, __null, }, | |||
128 | "patch", /* name of module */ | |||
129 | "", /* module documentation, may be NULL */ | |||
130 | -1, /* size of per-interpreter state of the module, or -1 if the module | |||
131 | keeps state in global variables. */ | |||
132 | PatchMethods.data()}; | |||
133 | PyObject* patch = PyModule_Create(&path)PyModule_Create2(&path, 1013); | |||
| ||||
| ||||
134 | if (!patch) { | |||
135 | throw python_error(); | |||
136 | } | |||
137 | if (PyModule_AddObject(module, "_fx", patch) != 0) { | |||
138 | throw python_error(); | |||
139 | } | |||
140 | } | |||
141 | } // namespace fx | |||
142 | } // namespace torch |
1 | #ifndef PyModule_Create2 |
2 | struct _object; |
3 | typedef struct _object PyObject; |
4 | PyObject* clang_analyzer_PyObject_New_Reference(); |
5 | PyObject* PyModule_Create2(PyModuleDef *def, int module_api_version) { |
6 | return clang_analyzer_PyObject_New_Reference(); |
7 | } |
8 | #else |
9 | #warning "API PyModule_Create2 is defined as a macro." |
10 | #endif |