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/logging.h> 37 #include <google/protobuf/stubs/common.h> 38 #include <google/protobuf/descriptor.h> 39 #include <google/protobuf/dynamic_message.h> 40 #include <google/protobuf/message.h> 41 #include <google/protobuf/pyext/descriptor.h> 42 #include <google/protobuf/pyext/descriptor_pool.h> 43 #include <google/protobuf/pyext/message.h> 44 #include <google/protobuf/pyext/repeated_composite_container.h> 45 #include <google/protobuf/pyext/repeated_scalar_container.h> 46 #include <google/protobuf/pyext/scoped_pyobject_ptr.h> 47 #include <google/protobuf/stubs/shared_ptr.h> 48 49 namespace google { 50 namespace protobuf { 51 namespace python { 52 53 namespace extension_dict { 54 55 PyObject* len(ExtensionDict* self) { 56 #if PY_MAJOR_VERSION >= 3 57 return PyLong_FromLong(PyDict_Size(self->values)); 58 #else 59 return PyInt_FromLong(PyDict_Size(self->values)); 60 #endif 61 } 62 63 // TODO(tibell): Use VisitCompositeField. 64 int ReleaseExtension(ExtensionDict* self, 65 PyObject* extension, 66 const FieldDescriptor* descriptor) { 67 if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { 68 if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 69 if (repeated_composite_container::Release( 70 reinterpret_cast<RepeatedCompositeContainer*>( 71 extension)) < 0) { 72 return -1; 73 } 74 } else { 75 if (repeated_scalar_container::Release( 76 reinterpret_cast<RepeatedScalarContainer*>( 77 extension)) < 0) { 78 return -1; 79 } 80 } 81 } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 82 if (cmessage::ReleaseSubMessage( 83 self->parent, descriptor, 84 reinterpret_cast<CMessage*>(extension)) < 0) { 85 return -1; 86 } 87 } 88 89 return 0; 90 } 91 92 PyObject* subscript(ExtensionDict* self, PyObject* key) { 93 const FieldDescriptor* descriptor = cmessage::GetExtensionDescriptor(key); 94 if (descriptor == NULL) { 95 return NULL; 96 } 97 if (!CheckFieldBelongsToMessage(descriptor, self->message)) { 98 return NULL; 99 } 100 101 if (descriptor->label() != FieldDescriptor::LABEL_REPEATED && 102 descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { 103 return cmessage::InternalGetScalar(self->message, descriptor); 104 } 105 106 PyObject* value = PyDict_GetItem(self->values, key); 107 if (value != NULL) { 108 Py_INCREF(value); 109 return value; 110 } 111 112 if (self->parent == NULL) { 113 // We are in "detached" state. Don't allow further modifications. 114 // TODO(amauryfa): Support adding non-scalars to a detached extension dict. 115 // This probably requires to store the type of the main message. 116 PyErr_SetObject(PyExc_KeyError, key); 117 return NULL; 118 } 119 120 if (descriptor->label() != FieldDescriptor::LABEL_REPEATED && 121 descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 122 PyObject* sub_message = cmessage::InternalGetSubMessage( 123 self->parent, descriptor); 124 if (sub_message == NULL) { 125 return NULL; 126 } 127 PyDict_SetItem(self->values, key, sub_message); 128 return sub_message; 129 } 130 131 if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { 132 if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 133 CMessageClass* message_class = cdescriptor_pool::GetMessageClass( 134 cmessage::GetDescriptorPoolForMessage(self->parent), 135 descriptor->message_type()); 136 if (message_class == NULL) { 137 return NULL; 138 } 139 PyObject* py_container = repeated_composite_container::NewContainer( 140 self->parent, descriptor, message_class); 141 if (py_container == NULL) { 142 return NULL; 143 } 144 PyDict_SetItem(self->values, key, py_container); 145 return py_container; 146 } else { 147 PyObject* py_container = repeated_scalar_container::NewContainer( 148 self->parent, descriptor); 149 if (py_container == NULL) { 150 return NULL; 151 } 152 PyDict_SetItem(self->values, key, py_container); 153 return py_container; 154 } 155 } 156 PyErr_SetString(PyExc_ValueError, "control reached unexpected line"); 157 return NULL; 158 } 159 160 int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) { 161 const FieldDescriptor* descriptor = cmessage::GetExtensionDescriptor(key); 162 if (descriptor == NULL) { 163 return -1; 164 } 165 if (!CheckFieldBelongsToMessage(descriptor, self->message)) { 166 return -1; 167 } 168 169 if (descriptor->label() != FieldDescriptor::LABEL_OPTIONAL || 170 descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 171 PyErr_SetString(PyExc_TypeError, "Extension is repeated and/or composite " 172 "type"); 173 return -1; 174 } 175 if (self->parent) { 176 cmessage::AssureWritable(self->parent); 177 if (cmessage::InternalSetScalar(self->parent, descriptor, value) < 0) { 178 return -1; 179 } 180 } 181 // TODO(tibell): We shouldn't write scalars to the cache. 182 PyDict_SetItem(self->values, key, value); 183 return 0; 184 } 185 186 PyObject* ClearExtension(ExtensionDict* self, PyObject* extension) { 187 const FieldDescriptor* descriptor = 188 cmessage::GetExtensionDescriptor(extension); 189 if (descriptor == NULL) { 190 return NULL; 191 } 192 PyObject* value = PyDict_GetItem(self->values, extension); 193 if (self->parent) { 194 if (value != NULL) { 195 if (ReleaseExtension(self, value, descriptor) < 0) { 196 return NULL; 197 } 198 } 199 if (ScopedPyObjectPtr(cmessage::ClearFieldByDescriptor( 200 self->parent, descriptor)) == NULL) { 201 return NULL; 202 } 203 } 204 if (PyDict_DelItem(self->values, extension) < 0) { 205 PyErr_Clear(); 206 } 207 Py_RETURN_NONE; 208 } 209 210 PyObject* HasExtension(ExtensionDict* self, PyObject* extension) { 211 const FieldDescriptor* descriptor = 212 cmessage::GetExtensionDescriptor(extension); 213 if (descriptor == NULL) { 214 return NULL; 215 } 216 if (self->parent) { 217 return cmessage::HasFieldByDescriptor(self->parent, descriptor); 218 } else { 219 int exists = PyDict_Contains(self->values, extension); 220 if (exists < 0) { 221 return NULL; 222 } 223 return PyBool_FromLong(exists); 224 } 225 } 226 227 PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) { 228 ScopedPyObjectPtr extensions_by_name(PyObject_GetAttrString( 229 reinterpret_cast<PyObject*>(self->parent), "_extensions_by_name")); 230 if (extensions_by_name == NULL) { 231 return NULL; 232 } 233 PyObject* result = PyDict_GetItem(extensions_by_name.get(), name); 234 if (result == NULL) { 235 Py_RETURN_NONE; 236 } else { 237 Py_INCREF(result); 238 return result; 239 } 240 } 241 242 PyObject* _FindExtensionByNumber(ExtensionDict* self, PyObject* number) { 243 ScopedPyObjectPtr extensions_by_number(PyObject_GetAttrString( 244 reinterpret_cast<PyObject*>(self->parent), "_extensions_by_number")); 245 if (extensions_by_number == NULL) { 246 return NULL; 247 } 248 PyObject* result = PyDict_GetItem(extensions_by_number.get(), number); 249 if (result == NULL) { 250 Py_RETURN_NONE; 251 } else { 252 Py_INCREF(result); 253 return result; 254 } 255 } 256 257 ExtensionDict* NewExtensionDict(CMessage *parent) { 258 ExtensionDict* self = reinterpret_cast<ExtensionDict*>( 259 PyType_GenericAlloc(&ExtensionDict_Type, 0)); 260 if (self == NULL) { 261 return NULL; 262 } 263 264 self->parent = parent; // Store a borrowed reference. 265 self->message = parent->message; 266 self->owner = parent->owner; 267 self->values = PyDict_New(); 268 return self; 269 } 270 271 void dealloc(ExtensionDict* self) { 272 Py_CLEAR(self->values); 273 self->owner.reset(); 274 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); 275 } 276 277 static PyMappingMethods MpMethods = { 278 (lenfunc)len, /* mp_length */ 279 (binaryfunc)subscript, /* mp_subscript */ 280 (objobjargproc)ass_subscript,/* mp_ass_subscript */ 281 }; 282 283 #define EDMETHOD(name, args, doc) { #name, (PyCFunction)name, args, doc } 284 static PyMethodDef Methods[] = { 285 EDMETHOD(ClearExtension, METH_O, "Clears an extension from the object."), 286 EDMETHOD(HasExtension, METH_O, "Checks if the object has an extension."), 287 EDMETHOD(_FindExtensionByName, METH_O, 288 "Finds an extension by name."), 289 EDMETHOD(_FindExtensionByNumber, METH_O, 290 "Finds an extension by field number."), 291 { NULL, NULL } 292 }; 293 294 } // namespace extension_dict 295 296 PyTypeObject ExtensionDict_Type = { 297 PyVarObject_HEAD_INIT(&PyType_Type, 0) 298 FULL_MODULE_NAME ".ExtensionDict", // tp_name 299 sizeof(ExtensionDict), // tp_basicsize 300 0, // tp_itemsize 301 (destructor)extension_dict::dealloc, // tp_dealloc 302 0, // tp_print 303 0, // tp_getattr 304 0, // tp_setattr 305 0, // tp_compare 306 0, // tp_repr 307 0, // tp_as_number 308 0, // tp_as_sequence 309 &extension_dict::MpMethods, // tp_as_mapping 310 PyObject_HashNotImplemented, // tp_hash 311 0, // tp_call 312 0, // tp_str 313 0, // tp_getattro 314 0, // tp_setattro 315 0, // tp_as_buffer 316 Py_TPFLAGS_DEFAULT, // tp_flags 317 "An extension dict", // tp_doc 318 0, // tp_traverse 319 0, // tp_clear 320 0, // tp_richcompare 321 0, // tp_weaklistoffset 322 0, // tp_iter 323 0, // tp_iternext 324 extension_dict::Methods, // tp_methods 325 0, // tp_members 326 0, // tp_getset 327 0, // tp_base 328 0, // tp_dict 329 0, // tp_descr_get 330 0, // tp_descr_set 331 0, // tp_dictoffset 332 0, // tp_init 333 }; 334 335 } // namespace python 336 } // namespace protobuf 337 } // namespace google 338