| 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 |