1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 // Author: anuraag (at) google.com (Anuraag Agrawal) 32 // Author: tibell (at) google.com (Johan Tibell) 33 34 #include <google/protobuf/pyext/extension_dict.h> 35 36 #include <google/protobuf/stubs/common.h> 37 #include <google/protobuf/descriptor.h> 38 #include <google/protobuf/dynamic_message.h> 39 #include <google/protobuf/message.h> 40 #include <google/protobuf/pyext/descriptor.h> 41 #include <google/protobuf/pyext/message.h> 42 #include <google/protobuf/pyext/repeated_composite_container.h> 43 #include <google/protobuf/pyext/repeated_scalar_container.h> 44 #include <google/protobuf/pyext/scoped_pyobject_ptr.h> 45 #include <google/protobuf/stubs/shared_ptr.h> 46 47 namespace google { 48 namespace protobuf { 49 namespace python { 50 51 extern google::protobuf::DynamicMessageFactory* global_message_factory; 52 53 namespace extension_dict { 54 55 // TODO(tibell): Always use self->message for clarity, just like in 56 // RepeatedCompositeContainer. 57 static google::protobuf::Message* GetMessage(ExtensionDict* self) { 58 if (self->parent != NULL) { 59 return self->parent->message; 60 } else { 61 return self->message; 62 } 63 } 64 65 CFieldDescriptor* InternalGetCDescriptorFromExtension(PyObject* extension) { 66 PyObject* cdescriptor = PyObject_GetAttrString(extension, "_cdescriptor"); 67 if (cdescriptor == NULL) { 68 PyErr_SetString(PyExc_KeyError, "Unregistered extension."); 69 return NULL; 70 } 71 if (!PyObject_TypeCheck(cdescriptor, &CFieldDescriptor_Type)) { 72 PyErr_SetString(PyExc_TypeError, "Not a CFieldDescriptor"); 73 Py_DECREF(cdescriptor); 74 return NULL; 75 } 76 CFieldDescriptor* descriptor = 77 reinterpret_cast<CFieldDescriptor*>(cdescriptor); 78 return descriptor; 79 } 80 81 PyObject* len(ExtensionDict* self) { 82 #if PY_MAJOR_VERSION >= 3 83 return PyLong_FromLong(PyDict_Size(self->values)); 84 #else 85 return PyInt_FromLong(PyDict_Size(self->values)); 86 #endif 87 } 88 89 // TODO(tibell): Use VisitCompositeField. 90 int ReleaseExtension(ExtensionDict* self, 91 PyObject* extension, 92 const google::protobuf::FieldDescriptor* descriptor) { 93 if (descriptor->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED) { 94 if (descriptor->cpp_type() == 95 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { 96 if (repeated_composite_container::Release( 97 reinterpret_cast<RepeatedCompositeContainer*>( 98 extension)) < 0) { 99 return -1; 100 } 101 } else { 102 if (repeated_scalar_container::Release( 103 reinterpret_cast<RepeatedScalarContainer*>( 104 extension)) < 0) { 105 return -1; 106 } 107 } 108 } else if (descriptor->cpp_type() == 109 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) { 110 if (cmessage::ReleaseSubMessage( 111 GetMessage(self), descriptor, 112 reinterpret_cast<CMessage*>(extension)) < 0) { 113 return -1; 114 } 115 } 116 117 return 0; 118 } 119 120 PyObject* subscript(ExtensionDict* self, PyObject* key) { 121 CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension( 122 key); 123 if (cdescriptor == NULL) { 124 return NULL; 125 } 126 ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor)); 127 const google::protobuf::FieldDescriptor* descriptor = cdescriptor->descriptor; 128 if (descriptor == NULL) { 129 return NULL; 130 } 131 if (descriptor->label() != FieldDescriptor::LABEL_REPEATED && 132 descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { 133 return cmessage::InternalGetScalar(self->parent, descriptor); 134 } 135 136 PyObject* value = PyDict_GetItem(self->values, key); 137 if (value != NULL) { 138 Py_INCREF(value); 139 return value; 140 } 141 142 if (descriptor->label() != FieldDescriptor::LABEL_REPEATED && 143 descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 144 PyObject* sub_message = cmessage::InternalGetSubMessage( 145 self->parent, cdescriptor); 146 if (sub_message == NULL) { 147 return NULL; 148 } 149 PyDict_SetItem(self->values, key, sub_message); 150 return sub_message; 151 } 152 153 if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { 154 if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 155 // COPIED 156 PyObject* py_container = PyObject_CallObject( 157 reinterpret_cast<PyObject*>(&RepeatedCompositeContainer_Type), 158 NULL); 159 if (py_container == NULL) { 160 return NULL; 161 } 162 RepeatedCompositeContainer* container = 163 reinterpret_cast<RepeatedCompositeContainer*>(py_container); 164 PyObject* field = cdescriptor->descriptor_field; 165 PyObject* message_type = PyObject_GetAttrString(field, "message_type"); 166 PyObject* concrete_class = PyObject_GetAttrString(message_type, 167 "_concrete_class"); 168 container->owner = self->owner; 169 container->parent = self->parent; 170 container->message = self->parent->message; 171 container->parent_field = cdescriptor; 172 container->subclass_init = concrete_class; 173 Py_DECREF(message_type); 174 PyDict_SetItem(self->values, key, py_container); 175 return py_container; 176 } else { 177 // COPIED 178 ScopedPyObjectPtr init_args(PyTuple_Pack(2, self->parent, cdescriptor)); 179 PyObject* py_container = PyObject_CallObject( 180 reinterpret_cast<PyObject*>(&RepeatedScalarContainer_Type), 181 init_args); 182 if (py_container == NULL) { 183 return NULL; 184 } 185 PyDict_SetItem(self->values, key, py_container); 186 return py_container; 187 } 188 } 189 PyErr_SetString(PyExc_ValueError, "control reached unexpected line"); 190 return NULL; 191 } 192 193 int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) { 194 CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension( 195 key); 196 if (cdescriptor == NULL) { 197 return -1; 198 } 199 ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor)); 200 const google::protobuf::FieldDescriptor* descriptor = cdescriptor->descriptor; 201 if (descriptor->label() != FieldDescriptor::LABEL_OPTIONAL || 202 descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 203 PyErr_SetString(PyExc_TypeError, "Extension is repeated and/or composite " 204 "type"); 205 return -1; 206 } 207 cmessage::AssureWritable(self->parent); 208 if (cmessage::InternalSetScalar(self->parent, descriptor, value) < 0) { 209 return -1; 210 } 211 // TODO(tibell): We shouldn't write scalars to the cache. 212 PyDict_SetItem(self->values, key, value); 213 return 0; 214 } 215 216 PyObject* ClearExtension(ExtensionDict* self, PyObject* extension) { 217 CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension( 218 extension); 219 if (cdescriptor == NULL) { 220 return NULL; 221 } 222 ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor)); 223 PyObject* value = PyDict_GetItem(self->values, extension); 224 if (value != NULL) { 225 if (ReleaseExtension(self, value, cdescriptor->descriptor) < 0) { 226 return NULL; 227 } 228 } 229 if (cmessage::ClearFieldByDescriptor(self->parent, 230 cdescriptor->descriptor) == NULL) { 231 return NULL; 232 } 233 if (PyDict_DelItem(self->values, extension) < 0) { 234 PyErr_Clear(); 235 } 236 Py_RETURN_NONE; 237 } 238 239 PyObject* HasExtension(ExtensionDict* self, PyObject* extension) { 240 CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension( 241 extension); 242 if (cdescriptor == NULL) { 243 return NULL; 244 } 245 ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor)); 246 PyObject* result = cmessage::HasFieldByDescriptor( 247 self->parent, cdescriptor->descriptor); 248 return result; 249 } 250 251 PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) { 252 ScopedPyObjectPtr extensions_by_name(PyObject_GetAttrString( 253 reinterpret_cast<PyObject*>(self->parent), "_extensions_by_name")); 254 if (extensions_by_name == NULL) { 255 return NULL; 256 } 257 PyObject* result = PyDict_GetItem(extensions_by_name, name); 258 if (result == NULL) { 259 Py_RETURN_NONE; 260 } else { 261 Py_INCREF(result); 262 return result; 263 } 264 } 265 266 int init(ExtensionDict* self, PyObject* args, PyObject* kwargs) { 267 self->parent = NULL; 268 self->message = NULL; 269 self->values = PyDict_New(); 270 return 0; 271 } 272 273 void dealloc(ExtensionDict* self) { 274 Py_CLEAR(self->values); 275 self->owner.reset(); 276 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); 277 } 278 279 static PyMappingMethods MpMethods = { 280 (lenfunc)len, /* mp_length */ 281 (binaryfunc)subscript, /* mp_subscript */ 282 (objobjargproc)ass_subscript,/* mp_ass_subscript */ 283 }; 284 285 #define EDMETHOD(name, args, doc) { #name, (PyCFunction)name, args, doc } 286 static PyMethodDef Methods[] = { 287 EDMETHOD(ClearExtension, METH_O, "Clears an extension from the object."), 288 EDMETHOD(HasExtension, METH_O, "Checks if the object has an extension."), 289 EDMETHOD(_FindExtensionByName, METH_O, 290 "Finds an extension by name."), 291 { NULL, NULL } 292 }; 293 294 } // namespace extension_dict 295 296 PyTypeObject ExtensionDict_Type = { 297 PyVarObject_HEAD_INIT(&PyType_Type, 0) 298 "google.protobuf.internal." 299 "cpp._message.ExtensionDict", // tp_name 300 sizeof(ExtensionDict), // tp_basicsize 301 0, // tp_itemsize 302 (destructor)extension_dict::dealloc, // tp_dealloc 303 0, // tp_print 304 0, // tp_getattr 305 0, // tp_setattr 306 0, // tp_compare 307 0, // tp_repr 308 0, // tp_as_number 309 0, // tp_as_sequence 310 &extension_dict::MpMethods, // tp_as_mapping 311 0, // tp_hash 312 0, // tp_call 313 0, // tp_str 314 0, // tp_getattro 315 0, // tp_setattro 316 0, // tp_as_buffer 317 Py_TPFLAGS_DEFAULT, // tp_flags 318 "An extension dict", // tp_doc 319 0, // tp_traverse 320 0, // tp_clear 321 0, // tp_richcompare 322 0, // tp_weaklistoffset 323 0, // tp_iter 324 0, // tp_iternext 325 extension_dict::Methods, // tp_methods 326 0, // tp_members 327 0, // tp_getset 328 0, // tp_base 329 0, // tp_dict 330 0, // tp_descr_get 331 0, // tp_descr_set 332 0, // tp_dictoffset 333 (initproc)extension_dict::init, // tp_init 334 }; 335 336 } // namespace python 337 } // namespace protobuf 338 } // namespace google 339