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