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: petar@google.com (Petar Petrov)
32 
33 #include <Python.h>
34 #include <string>
35 
36 #include <google/protobuf/descriptor.pb.h>
37 #include <google/protobuf/pyext/descriptor.h>
38 #include <google/protobuf/pyext/scoped_pyobject_ptr.h>
39 
40 #define C(str) const_cast<char*>(str)
41 
42 #if PY_MAJOR_VERSION >= 3
43   #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
44   #define PyInt_FromLong PyLong_FromLong
45   #if PY_VERSION_HEX < 0x03030000
46     #error "Python 3.0 - 3.2 are not supported."
47   #else
48   #define PyString_AsString(ob) \
49     (PyUnicode_Check(ob)? PyUnicode_AsUTF8(ob): PyBytes_AS_STRING(ob))
50   #endif
51 #endif
52 
53 namespace google {
54 namespace protobuf {
55 namespace python {
56 
57 
58 #ifndef PyVarObject_HEAD_INIT
59 #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
60 #endif
61 #ifndef Py_TYPE
62 #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
63 #endif
64 
65 
66 static google::protobuf::DescriptorPool* g_descriptor_pool = NULL;
67 
68 namespace cfield_descriptor {
69 
Dealloc(CFieldDescriptor * self)70 static void Dealloc(CFieldDescriptor* self) {
71   Py_CLEAR(self->descriptor_field);
72   Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
73 }
74 
GetFullName(CFieldDescriptor * self,void * closure)75 static PyObject* GetFullName(CFieldDescriptor* self, void *closure) {
76   return PyString_FromStringAndSize(
77       self->descriptor->full_name().c_str(),
78       self->descriptor->full_name().size());
79 }
80 
GetName(CFieldDescriptor * self,void * closure)81 static PyObject* GetName(CFieldDescriptor *self, void *closure) {
82   return PyString_FromStringAndSize(
83       self->descriptor->name().c_str(),
84       self->descriptor->name().size());
85 }
86 
GetCppType(CFieldDescriptor * self,void * closure)87 static PyObject* GetCppType(CFieldDescriptor *self, void *closure) {
88   return PyInt_FromLong(self->descriptor->cpp_type());
89 }
90 
GetLabel(CFieldDescriptor * self,void * closure)91 static PyObject* GetLabel(CFieldDescriptor *self, void *closure) {
92   return PyInt_FromLong(self->descriptor->label());
93 }
94 
GetID(CFieldDescriptor * self,void * closure)95 static PyObject* GetID(CFieldDescriptor *self, void *closure) {
96   return PyLong_FromVoidPtr(self);
97 }
98 
99 static PyGetSetDef Getters[] = {
100   { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL},
101   { C("name"), (getter)GetName, NULL, "last name", NULL},
102   { C("cpp_type"), (getter)GetCppType, NULL, "C++ Type", NULL},
103   { C("label"), (getter)GetLabel, NULL, "Label", NULL},
104   { C("id"), (getter)GetID, NULL, "ID", NULL},
105   {NULL}
106 };
107 
108 }  // namespace cfield_descriptor
109 
110 PyTypeObject CFieldDescriptor_Type = {
111   PyVarObject_HEAD_INIT(&PyType_Type, 0)
112   C("google.protobuf.internal."
113     "_net_proto2___python."
114     "CFieldDescriptor"),                // tp_name
115   sizeof(CFieldDescriptor),             // tp_basicsize
116   0,                                    // tp_itemsize
117   (destructor)cfield_descriptor::Dealloc,  // tp_dealloc
118   0,                                    // tp_print
119   0,                                    // tp_getattr
120   0,                                    // tp_setattr
121   0,                                    // tp_compare
122   0,                                    // tp_repr
123   0,                                    // tp_as_number
124   0,                                    // tp_as_sequence
125   0,                                    // tp_as_mapping
126   0,                                    // tp_hash
127   0,                                    // tp_call
128   0,                                    // tp_str
129   0,                                    // tp_getattro
130   0,                                    // tp_setattro
131   0,                                    // tp_as_buffer
132   Py_TPFLAGS_DEFAULT,                   // tp_flags
133   C("A Field Descriptor"),              // tp_doc
134   0,                                    // tp_traverse
135   0,                                    // tp_clear
136   0,                                    // tp_richcompare
137   0,                                    // tp_weaklistoffset
138   0,                                    // tp_iter
139   0,                                    // tp_iternext
140   0,                                    // tp_methods
141   0,                                    // tp_members
142   cfield_descriptor::Getters,           // tp_getset
143   0,                                    // tp_base
144   0,                                    // tp_dict
145   0,                                    // tp_descr_get
146   0,                                    // tp_descr_set
147   0,                                    // tp_dictoffset
148   0,                                    // tp_init
149   PyType_GenericAlloc,                  // tp_alloc
150   PyType_GenericNew,                    // tp_new
151   PyObject_Del,                         // tp_free
152 };
153 
154 namespace cdescriptor_pool {
155 
Dealloc(CDescriptorPool * self)156 static void Dealloc(CDescriptorPool* self) {
157   Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
158 }
159 
NewCDescriptor(const google::protobuf::FieldDescriptor * field_descriptor)160 static PyObject* NewCDescriptor(
161     const google::protobuf::FieldDescriptor* field_descriptor) {
162   CFieldDescriptor* cfield_descriptor = PyObject_New(
163       CFieldDescriptor, &CFieldDescriptor_Type);
164   if (cfield_descriptor == NULL) {
165     return NULL;
166   }
167   cfield_descriptor->descriptor = field_descriptor;
168   cfield_descriptor->descriptor_field = NULL;
169 
170   return reinterpret_cast<PyObject*>(cfield_descriptor);
171 }
172 
FindFieldByName(CDescriptorPool * self,PyObject * name)173 PyObject* FindFieldByName(CDescriptorPool* self, PyObject* name) {
174   const char* full_field_name = PyString_AsString(name);
175   if (full_field_name == NULL) {
176     return NULL;
177   }
178 
179   const google::protobuf::FieldDescriptor* field_descriptor = NULL;
180 
181   field_descriptor = self->pool->FindFieldByName(full_field_name);
182 
183   if (field_descriptor == NULL) {
184     PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
185                  full_field_name);
186     return NULL;
187   }
188 
189   return NewCDescriptor(field_descriptor);
190 }
191 
FindExtensionByName(CDescriptorPool * self,PyObject * arg)192 PyObject* FindExtensionByName(CDescriptorPool* self, PyObject* arg) {
193   const char* full_field_name = PyString_AsString(arg);
194   if (full_field_name == NULL) {
195     return NULL;
196   }
197 
198   const google::protobuf::FieldDescriptor* field_descriptor =
199       self->pool->FindExtensionByName(full_field_name);
200   if (field_descriptor == NULL) {
201     PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
202                  full_field_name);
203     return NULL;
204   }
205 
206   return NewCDescriptor(field_descriptor);
207 }
208 
209 static PyMethodDef Methods[] = {
210   { C("FindFieldByName"),
211     (PyCFunction)FindFieldByName,
212     METH_O,
213     C("Searches for a field descriptor by full name.") },
214   { C("FindExtensionByName"),
215     (PyCFunction)FindExtensionByName,
216     METH_O,
217     C("Searches for extension descriptor by full name.") },
218   {NULL}
219 };
220 
221 }  // namespace cdescriptor_pool
222 
223 PyTypeObject CDescriptorPool_Type = {
224   PyVarObject_HEAD_INIT(&PyType_Type, 0)
225   C("google.protobuf.internal."
226     "_net_proto2___python."
227     "CFieldDescriptor"),               // tp_name
228   sizeof(CDescriptorPool),             // tp_basicsize
229   0,                                   // tp_itemsize
230   (destructor)cdescriptor_pool::Dealloc,  // tp_dealloc
231   0,                                   // tp_print
232   0,                                   // tp_getattr
233   0,                                   // tp_setattr
234   0,                                   // tp_compare
235   0,                                   // tp_repr
236   0,                                   // tp_as_number
237   0,                                   // tp_as_sequence
238   0,                                   // tp_as_mapping
239   0,                                   // tp_hash
240   0,                                   // tp_call
241   0,                                   // tp_str
242   0,                                   // tp_getattro
243   0,                                   // tp_setattro
244   0,                                   // tp_as_buffer
245   Py_TPFLAGS_DEFAULT,                  // tp_flags
246   C("A Descriptor Pool"),              // tp_doc
247   0,                                   // tp_traverse
248   0,                                   // tp_clear
249   0,                                   // tp_richcompare
250   0,                                   // tp_weaklistoffset
251   0,                                   // tp_iter
252   0,                                   // tp_iternext
253   cdescriptor_pool::Methods,           // tp_methods
254   0,                                   // tp_members
255   0,                                   // tp_getset
256   0,                                   // tp_base
257   0,                                   // tp_dict
258   0,                                   // tp_descr_get
259   0,                                   // tp_descr_set
260   0,                                   // tp_dictoffset
261   0,                                   // tp_init
262   PyType_GenericAlloc,                 // tp_alloc
263   PyType_GenericNew,                   // tp_new
264   PyObject_Del,                        // tp_free
265 };
266 
GetDescriptorPool()267 google::protobuf::DescriptorPool* GetDescriptorPool() {
268   if (g_descriptor_pool == NULL) {
269     g_descriptor_pool = new google::protobuf::DescriptorPool(
270         google::protobuf::DescriptorPool::generated_pool());
271   }
272   return g_descriptor_pool;
273 }
274 
Python_NewCDescriptorPool(PyObject * ignored,PyObject * args)275 PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args) {
276   CDescriptorPool* cdescriptor_pool = PyObject_New(
277       CDescriptorPool, &CDescriptorPool_Type);
278   if (cdescriptor_pool == NULL) {
279     return NULL;
280   }
281   cdescriptor_pool->pool = GetDescriptorPool();
282   return reinterpret_cast<PyObject*>(cdescriptor_pool);
283 }
284 
285 
286 // Collects errors that occur during proto file building to allow them to be
287 // propagated in the python exception instead of only living in ERROR logs.
288 class BuildFileErrorCollector : public google::protobuf::DescriptorPool::ErrorCollector {
289  public:
BuildFileErrorCollector()290   BuildFileErrorCollector() : error_message(""), had_errors(false) {}
291 
AddError(const string & filename,const string & element_name,const Message * descriptor,ErrorLocation location,const string & message)292   void AddError(const string& filename, const string& element_name,
293                 const Message* descriptor, ErrorLocation location,
294                 const string& message) {
295     // Replicates the logging behavior that happens in the C++ implementation
296     // when an error collector is not passed in.
297     if (!had_errors) {
298       error_message +=
299           ("Invalid proto descriptor for file \"" + filename + "\":\n");
300     }
301     // As this only happens on failure and will result in the program not
302     // running at all, no effort is made to optimize this string manipulation.
303     error_message += ("  " + element_name + ": " + message + "\n");
304   }
305 
306   string error_message;
307   bool had_errors;
308 };
309 
Python_BuildFile(PyObject * ignored,PyObject * arg)310 PyObject* Python_BuildFile(PyObject* ignored, PyObject* arg) {
311   char* message_type;
312   Py_ssize_t message_len;
313 
314   if (PyBytes_AsStringAndSize(arg, &message_type, &message_len) < 0) {
315     return NULL;
316   }
317 
318   google::protobuf::FileDescriptorProto file_proto;
319   if (!file_proto.ParseFromArray(message_type, message_len)) {
320     PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
321     return NULL;
322   }
323 
324   if (google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
325           file_proto.name()) != NULL) {
326     Py_RETURN_NONE;
327   }
328 
329   BuildFileErrorCollector error_collector;
330   const google::protobuf::FileDescriptor* descriptor =
331       GetDescriptorPool()->BuildFileCollectingErrors(file_proto,
332                                                      &error_collector);
333   if (descriptor == NULL) {
334     PyErr_Format(PyExc_TypeError,
335                  "Couldn't build proto file into descriptor pool!\n%s",
336                  error_collector.error_message.c_str());
337     return NULL;
338   }
339 
340   Py_RETURN_NONE;
341 }
342 
InitDescriptor()343 bool InitDescriptor() {
344   CFieldDescriptor_Type.tp_new = PyType_GenericNew;
345   if (PyType_Ready(&CFieldDescriptor_Type) < 0)
346     return false;
347 
348   CDescriptorPool_Type.tp_new = PyType_GenericNew;
349   if (PyType_Ready(&CDescriptorPool_Type) < 0)
350     return false;
351 
352   return true;
353 }
354 
355 }  // namespace python
356 }  // namespace protobuf
357 }  // namespace google
358