File: | .cache/bazel/_bazel_alan/39be661231df2a680c9b74265384c13c/execroot/org_tensorflow/tensorflow/python/util/fast_module_type.cc |
Warning: | line 192, column 23 PyObject ownership leak with reference count of 1 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* Copyright 2021 The TensorFlow Authors. All Rights Reserved. | |||
2 | ||||
3 | Licensed under the Apache License, Version 2.0 (the "License"); | |||
4 | you may not use this file except in compliance with the License. | |||
5 | You may obtain a copy of the License at | |||
6 | ||||
7 | http://www.apache.org/licenses/LICENSE-2.0 | |||
8 | ||||
9 | Unless required by applicable law or agreed to in writing, software | |||
10 | distributed under the License is distributed on an "AS IS" BASIS, | |||
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
12 | See the License for the specific language governing permissions and | |||
13 | limitations under the License. | |||
14 | ==============================================================================*/ | |||
15 | #include <Python.h> | |||
16 | ||||
17 | #include "absl/container/flat_hash_map.h" | |||
18 | #include "pybind11/pybind11.h" | |||
19 | #include "tensorflow/core/platform/logging.h" | |||
20 | ||||
21 | namespace py = pybind11; | |||
22 | constexpr int PY_MODULE_TYPE_TP_BASIC_SIZE = 56; | |||
23 | ||||
24 | struct FastModuleObject { | |||
25 | // A dummy array that ensures enough size is reserved for FastModuleObject, | |||
26 | // because it's inherited from PyModuleObject. | |||
27 | const std::array<char, PY_MODULE_TYPE_TP_BASIC_SIZE> opaque_base_fields; | |||
28 | // A cache that helps reduce attribute lookup overhead. | |||
29 | absl::flat_hash_map<PyObject *, PyObject *> attr_map; | |||
30 | // pointer to the external getattribute function | |||
31 | PyObject *cb_getattribute = nullptr; | |||
32 | // pointer to the external getattr function | |||
33 | PyObject *cb_getattr = nullptr; | |||
34 | // static PyTypeObject type; | |||
35 | ||||
36 | FastModuleObject() = delete; | |||
37 | ~FastModuleObject() = delete; | |||
38 | static FastModuleObject *UncheckedCast(PyObject *obj); | |||
39 | }; | |||
40 | ||||
41 | static int FastModule_init(FastModuleObject *self, PyObject *args, | |||
42 | PyObject *kwds) { | |||
43 | DCHECK_EQ(PY_MODULE_TYPE_TP_BASIC_SIZE, PyModule_Type.tp_basicsize)while (false && ((void)(PY_MODULE_TYPE_TP_BASIC_SIZE) , (void)(PyModule_Type.tp_basicsize), 0)) ::tensorflow::internal ::LogMessageFatal("tensorflow/python/util/fast_module_type.cc" , 43); | |||
44 | if (PyModule_Type.tp_init(reinterpret_cast<PyObject *>(self), args, kwds) < 0) | |||
45 | return -1; | |||
46 | new (&(self->attr_map)) absl::flat_hash_map<PyObject *, PyObject *>(); | |||
47 | return 0; | |||
48 | } | |||
49 | ||||
50 | // Parses the input as a callable and checks the result. | |||
51 | static PyObject *ParseFunc(PyObject *args) { | |||
52 | PyObject *func; | |||
53 | if (!PyArg_ParseTuple(args, "O:set_callback", &func)) return nullptr; | |||
54 | if (!PyCallable_Check(func)) { | |||
55 | PyErr_SetString(PyExc_TypeError, "input args must be callable"); | |||
56 | return nullptr; | |||
57 | } | |||
58 | Py_INCREF(func)_Py_INCREF(((PyObject*)(func))); // Add a reference to new callback | |||
59 | return func; | |||
60 | } | |||
61 | ||||
62 | // Sets the pointer 'cb_getattribute' in the FastModuleObject object | |||
63 | // corresponding to 'self'. | |||
64 | static PyObject *SetGetattributeCallback(PyObject *self, PyObject *args) { | |||
65 | PyObject *func = ParseFunc(args); | |||
66 | // Dispose of previous callback | |||
67 | Py_XDECREF(FastModuleObject::UncheckedCast(self)->cb_getattribute)_Py_XDECREF(((PyObject*)(FastModuleObject::UncheckedCast(self )->cb_getattribute))); | |||
68 | // Remember new callback | |||
69 | FastModuleObject::UncheckedCast(self)->cb_getattribute = func; | |||
70 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | |||
71 | } | |||
72 | ||||
73 | // Sets the pointer 'cb_getattr' in the FastModuleObject object | |||
74 | // corresponding to 'self'. | |||
75 | static PyObject *SetGetattrCallback(PyObject *self, PyObject *args) { | |||
76 | PyObject *func = ParseFunc(args); | |||
77 | // Dispose of previous callback | |||
78 | Py_XDECREF(FastModuleObject::UncheckedCast(self)->cb_getattr)_Py_XDECREF(((PyObject*)(FastModuleObject::UncheckedCast(self )->cb_getattr))); | |||
79 | // Remember new callback | |||
80 | FastModuleObject::UncheckedCast(self)->cb_getattr = func; | |||
81 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | |||
82 | } | |||
83 | ||||
84 | // Inserts or updates a key-value pair in the cache 'attr_map' | |||
85 | // of the FastModuleObject object corresponding to 'self'. | |||
86 | static PyObject *FastDictInsert(FastModuleObject *self, PyObject *args) { | |||
87 | PyObject *name, *value; | |||
88 | if (!PyArg_ParseTuple(args, "OO", &name, &value)) { | |||
89 | PyErr_SetString(PyExc_TypeError, "_fastdict_insert: incorrect inputs"); | |||
90 | return nullptr; | |||
91 | } | |||
92 | auto &attr_map = self->attr_map; | |||
93 | if (attr_map.find(name) != attr_map.end()) { | |||
94 | Py_DECREF(name)_Py_DECREF(((PyObject*)(name))); | |||
95 | Py_DECREF(value)_Py_DECREF(((PyObject*)(value))); | |||
96 | } | |||
97 | attr_map.insert_or_assign(name, value); | |||
98 | // Increment the reference count | |||
99 | Py_INCREF(name)_Py_INCREF(((PyObject*)(name))); | |||
100 | Py_INCREF(value)_Py_INCREF(((PyObject*)(value))); | |||
101 | // Properly handle returning Py_None | |||
102 | Py_RETURN_NONEreturn _Py_INCREF(((PyObject*)((&_Py_NoneStruct)))), (& _Py_NoneStruct); | |||
103 | } | |||
104 | ||||
105 | // Gets a value from a key in the cache 'attr_map' | |||
106 | // of the FastModuleObject object corresponding to 'self'. | |||
107 | static PyObject *FastDictGet(FastModuleObject *self, PyObject *args) { | |||
108 | PyObject *name; | |||
109 | if (!PyArg_ParseTuple(args, "O", &name)) { | |||
110 | PyErr_SetString(PyExc_TypeError, "_fastdict_get: incorrect inputs"); | |||
111 | return nullptr; | |||
112 | } | |||
113 | auto &attr_map = self->attr_map; | |||
114 | auto result = attr_map.find(name); | |||
115 | if (result != attr_map.end()) { | |||
116 | PyObject *value = result->second; | |||
117 | Py_INCREF(value)_Py_INCREF(((PyObject*)(value))); | |||
118 | return value; | |||
119 | } | |||
120 | // Copied from CPython's moduleobject.c | |||
121 | PyErr_Format(PyExc_KeyError, "module has no attribute '%U'", name); | |||
122 | return nullptr; | |||
123 | } | |||
124 | ||||
125 | // Returns true if a key exists in the cache 'attr_map' | |||
126 | // of the FastModuleObject object corresponding to 'self', | |||
127 | // otherwise returns false. | |||
128 | static PyObject *FastDictContains(FastModuleObject *self, PyObject *args) { | |||
129 | PyObject *name; | |||
130 | if (!PyArg_ParseTuple(args, "O", &name)) { | |||
131 | PyErr_SetString(PyExc_TypeError, "_fastdict_key_in: incorrect inputs"); | |||
132 | return nullptr; | |||
133 | } | |||
134 | const auto &attr_map = self->attr_map; | |||
135 | const auto result = attr_map.contains(name); | |||
136 | if (result) { | |||
137 | // Properly handle returning Py_True | |||
138 | Py_RETURN_TRUEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_TrueStruct )))), ((PyObject *) &_Py_TrueStruct); | |||
139 | } | |||
140 | // Properly handle returning Py_False | |||
141 | Py_RETURN_FALSEreturn _Py_INCREF(((PyObject*)(((PyObject *) &_Py_FalseStruct )))), ((PyObject *) &_Py_FalseStruct); | |||
142 | } | |||
143 | ||||
144 | // Calls a function 'func' with inputs 'self' and 'args'. | |||
145 | static PyObject *CallFunc(FastModuleObject *self, PyObject *args, | |||
146 | PyObject *func) { | |||
147 | if (func == nullptr) { | |||
148 | PyErr_SetString(PyExc_NameError, | |||
149 | "Attempting to call a callback that was not defined"); | |||
150 | return nullptr; | |||
151 | } | |||
152 | PyObject *name; | |||
153 | if (!PyArg_ParseTuple(args, "O", &name)) { | |||
154 | PyErr_SetString(PyExc_TypeError, "CallFunc: incorrect inputs"); | |||
155 | return nullptr; | |||
156 | } | |||
157 | PyObject *arglist = Py_BuildValue("(OO)", self, name); | |||
158 | auto result = PyObject_CallObject(func, arglist); | |||
159 | Py_DECREF(arglist)_Py_DECREF(((PyObject*)(arglist))); | |||
160 | return result; | |||
161 | } | |||
162 | ||||
163 | static PyMethodDef FastModule_methods[] = { | |||
164 | {"_fastdict_insert", reinterpret_cast<PyCFunction>(FastDictInsert), | |||
165 | METH_VARARGS0x0001, "Registers a method to the fast lookup table."}, | |||
166 | {"_fastdict_get", reinterpret_cast<PyCFunction>(FastDictGet), METH_VARARGS0x0001, | |||
167 | "Gets a method from the fast lookup table."}, | |||
168 | {"_fastdict_key_in", reinterpret_cast<PyCFunction>(FastDictContains), | |||
169 | METH_VARARGS0x0001, "Checks if a method exists in the fast lookup table."}, | |||
170 | {"set_getattribute_callback", SetGetattributeCallback, METH_VARARGS0x0001, | |||
171 | "Defines the callback function to replace __getattribute__"}, | |||
172 | {"set_getattr_callback", SetGetattrCallback, METH_VARARGS0x0001, | |||
173 | "Defines the callback function to replace __getattr__"}, | |||
174 | {nullptr, nullptr, 0, nullptr}, | |||
175 | }; | |||
176 | ||||
177 | // Attempts to get the attribute based on 'name' as the key in cache 'attr_map' | |||
178 | // of the FastModuleObject object corresponding to 'module'. | |||
179 | // If the lookup fails in the cache, either uses | |||
180 | // a user-defined callback 'cb_getattribute' | |||
181 | // or the default 'tp_getattro' function to look for the attribute. | |||
182 | static PyObject *FastTpGetattro(PyObject *module, PyObject *name) { | |||
183 | FastModuleObject *fast_module = FastModuleObject::UncheckedCast(module); | |||
184 | auto &attr_map = fast_module->attr_map; | |||
185 | auto it = attr_map.find(name); | |||
186 | // If the attribute lookup is successful in the cache, directly return it. | |||
187 | if (it != attr_map.end()) { | |||
| ||||
188 | PyObject *value = it->second; | |||
189 | Py_INCREF(value)_Py_INCREF(((PyObject*)(value))); | |||
190 | return value; | |||
191 | } | |||
192 | PyObject *arglist = Py_BuildValue("(O)", name); | |||
| ||||
193 | PyObject *result; | |||
194 | // Prefer the customized callback function over the default function. | |||
195 | if (fast_module->cb_getattribute != nullptr) { | |||
196 | result = CallFunc(fast_module, arglist, fast_module->cb_getattribute); | |||
197 | } else { | |||
198 | result = PyModule_Type.tp_getattro(module, name); | |||
199 | } | |||
200 | // Return result if it's found | |||
201 | if (result != nullptr) { | |||
202 | return result; | |||
203 | } | |||
204 | // If the default lookup fails and an AttributeError is raised, | |||
205 | // clear the error status before using the __getattr__ callback function. | |||
206 | auto is_error = PyErr_Occurred(); | |||
207 | if (is_error && PyErr_ExceptionMatches(PyExc_AttributeError) && | |||
208 | fast_module->cb_getattr != nullptr) { | |||
209 | PyErr_Clear(); | |||
210 | return CallFunc(fast_module, arglist, fast_module->cb_getattr); | |||
211 | } | |||
212 | // If all options were used up | |||
213 | return result; | |||
214 | } | |||
215 | ||||
216 | // Customized destructor for FastModuleType.tp_dealloc | |||
217 | // In addition to default behavior it also clears up the contents in attr_map. | |||
218 | static void FastModuleObjectDealloc(PyObject *module) { | |||
219 | auto &attr_map = FastModuleObject::UncheckedCast(module)->attr_map; | |||
220 | for (auto &it : attr_map) { | |||
221 | Py_DECREF(it.first)_Py_DECREF(((PyObject*)(it.first))); | |||
222 | Py_DECREF(it.second)_Py_DECREF(((PyObject*)(it.second))); | |||
223 | } | |||
224 | attr_map.~flat_hash_map<PyObject *, PyObject *>(); | |||
225 | Py_TYPE(module)(((PyObject*)(module))->ob_type)->tp_free(module); | |||
226 | } | |||
227 | ||||
228 | static PyTypeObject FastModuleType = []() { | |||
229 | PyTypeObject obj = {PyVarObject_HEAD_INIT(&PyType_Type, 0){ { 1, &PyType_Type }, 0 },}; | |||
230 | obj.tp_name = "fast_module_type.FastModuleType"; | |||
231 | obj.tp_basicsize = sizeof(FastModuleObject); | |||
232 | obj.tp_itemsize = 0; | |||
233 | obj.tp_dealloc = FastModuleObjectDealloc; | |||
234 | obj.tp_getattro = FastTpGetattro; | |||
235 | obj.tp_flags = Py_TPFLAGS_DEFAULT( 0 | (1UL << 18) | 0) | Py_TPFLAGS_BASETYPE(1UL << 10); | |||
236 | obj.tp_doc = "FastModuleType objects"; | |||
237 | obj.tp_methods = FastModule_methods; | |||
238 | obj.tp_init = reinterpret_cast<initproc>(FastModule_init); | |||
239 | return obj; | |||
240 | }(); | |||
241 | ||||
242 | // Returns true if the type of 'obj' or any of its parent class | |||
243 | // is equal to 'target'. Otherwise returns false. | |||
244 | bool IsAnyBaseSameType(const PyObject *obj, const PyTypeObject *target) { | |||
245 | auto *tp = Py_TYPE(obj)(((PyObject*)(obj))->ob_type); | |||
246 | while (true) { | |||
247 | if (tp == target) return true; | |||
248 | // If the default type is found, there is no need to search further | |||
249 | if (tp == &PyBaseObject_Type) break; | |||
250 | tp = tp->tp_base; | |||
251 | } | |||
252 | return false; | |||
253 | } | |||
254 | ||||
255 | // Casts 'obj' to 'FastModuleObject *'. | |||
256 | // Conducts a check only in non-optimized builds. | |||
257 | FastModuleObject *FastModuleObject::UncheckedCast(PyObject *obj) { | |||
258 | DCHECK(IsAnyBaseSameType(obj, &FastModuleType))while (false && (IsAnyBaseSameType(obj, &FastModuleType ))) ::tensorflow::internal::LogMessageFatal("tensorflow/python/util/fast_module_type.cc" , 258); | |||
259 | return reinterpret_cast<FastModuleObject *>(obj); | |||
260 | } | |||
261 | ||||
262 | PYBIND11_MODULE(fast_module_type, m)static ::pybind11::module_::module_def pybind11_module_def_fast_module_type ; __attribute__ ((__unused__)) static void pybind11_init_fast_module_type (::pybind11::module_ &); extern "C" __attribute__ ((__unused__ )) __attribute__ ((visibility("default"))) PyObject *PyInit_fast_module_type (); extern "C" __attribute__ ((visibility("default"))) PyObject *PyInit_fast_module_type() { { const char *compiled_ver = "3" "." "8"; const char *runtime_ver = Py_GetVersion(); size_t len = std::strlen(compiled_ver); if (std::strncmp(runtime_ver, compiled_ver , len) != 0 || (runtime_ver[len] >= '0' && runtime_ver [len] <= '9')) { PyErr_Format(PyExc_ImportError, "Python version mismatch: module was compiled for Python %s, " "but the interpreter version is incompatible: %s.", compiled_ver , runtime_ver); return nullptr; } } pybind11::detail::get_internals (); auto m = ::pybind11::module_::create_extension_module( "fast_module_type" , nullptr, &pybind11_module_def_fast_module_type); try { pybind11_init_fast_module_type (m); return m.ptr(); } catch (pybind11::error_already_set & e) { PyErr_SetString(PyExc_ImportError, e.what()); return nullptr ; } catch (const std::exception &e) { PyErr_SetString(PyExc_ImportError , e.what()); return nullptr; } } void pybind11_init_fast_module_type (::pybind11::module_ &m) { | |||
263 | FastModuleType.tp_base = &PyModule_Type; | |||
264 | FastModuleType.tp_setattro = [](PyObject *module, PyObject *name, | |||
265 | PyObject *value) -> int { | |||
266 | auto &attr_map = FastModuleObject::UncheckedCast(module)->attr_map; | |||
267 | if (attr_map.find(name) != attr_map.end()) { | |||
268 | Py_DECREF(name)_Py_DECREF(((PyObject*)(name))); | |||
269 | Py_DECREF(value)_Py_DECREF(((PyObject*)(value))); | |||
270 | } | |||
271 | attr_map.insert_or_assign(name, value); | |||
272 | // Increment the reference count | |||
273 | Py_INCREF(name)_Py_INCREF(((PyObject*)(name))); | |||
274 | Py_INCREF(value)_Py_INCREF(((PyObject*)(value))); | |||
275 | PyObject_GenericSetAttr(module, name, value); | |||
276 | return 0; | |||
277 | }; | |||
278 | ||||
279 | m.doc() = R"pbdoc( | |||
280 | fast_module_type | |||
281 | ----- | |||
282 | )pbdoc"; | |||
283 | // Use getter function to hold attributes rather than pybind11's m.attr due to | |||
284 | // b/145559202. | |||
285 | m.def( | |||
286 | "get_fast_module_type_class", | |||
287 | []() { | |||
288 | return py::cast<py::object>( | |||
289 | reinterpret_cast<PyObject *>(&FastModuleType)); | |||
290 | }, | |||
291 | py::return_value_policy::reference); | |||
292 | } |
1 | #ifndef Py_BuildValue |
2 | struct _object; |
3 | typedef struct _object PyObject; |
4 | PyObject* clang_analyzer_PyObject_New_Reference(); |
5 | PyObject* Py_BuildValue(const char *format, ...) { |
6 | return clang_analyzer_PyObject_New_Reference(); |
7 | } |
8 | #else |
9 | #warning "API Py_BuildValue is defined as a macro." |
10 | #endif |