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@google.com (Anuraag Agrawal) 32// Author: tibell@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 47namespace google { 48namespace protobuf { 49namespace python { 50 51extern google::protobuf::DynamicMessageFactory* global_message_factory; 52 53namespace extension_dict { 54 55// TODO(tibell): Always use self->message for clarity, just like in 56// RepeatedCompositeContainer. 57static 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 65CFieldDescriptor* 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 81PyObject* 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. 90int 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 120PyObject* 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 193int 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 216PyObject* 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 239PyObject* 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 251PyObject* _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 266int 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 273void dealloc(ExtensionDict* self) { 274 Py_CLEAR(self->values); 275 self->owner.reset(); 276 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); 277} 278 279static 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 } 286static 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 296PyTypeObject 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