• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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.
GetMessage(ExtensionDict * self)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 
InternalGetCDescriptorFromExtension(PyObject * extension)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 
len(ExtensionDict * self)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.
ReleaseExtension(ExtensionDict * self,PyObject * extension,const google::protobuf::FieldDescriptor * descriptor)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 
subscript(ExtensionDict * self,PyObject * key)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 
ass_subscript(ExtensionDict * self,PyObject * key,PyObject * value)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 
ClearExtension(ExtensionDict * self,PyObject * extension)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 
HasExtension(ExtensionDict * self,PyObject * extension)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 
_FindExtensionByName(ExtensionDict * self,PyObject * name)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 
init(ExtensionDict * self,PyObject * args,PyObject * kwargs)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 
dealloc(ExtensionDict * self)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