1 /*
2  * Copyright (c) 2009-2021, Google LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of Google LLC nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "python/descriptor_pool.h"
29 
30 #include "google/protobuf/descriptor.upbdefs.h"
31 #include "python/convert.h"
32 #include "python/descriptor.h"
33 #include "python/message.h"
34 #include "python/protobuf.h"
35 #include "upb/reflection/def.h"
36 #include "upb/util/def_to_proto.h"
37 
38 // -----------------------------------------------------------------------------
39 // DescriptorPool
40 // -----------------------------------------------------------------------------
41 
42 typedef struct {
43   PyObject_HEAD;
44   upb_DefPool* symtab;
45   PyObject* db;  // The DescriptorDatabase underlying this pool.  May be NULL.
46 } PyUpb_DescriptorPool;
47 
PyUpb_DescriptorPool_GetDefaultPool(void)48 PyObject* PyUpb_DescriptorPool_GetDefaultPool(void) {
49   PyUpb_ModuleState* s = PyUpb_ModuleState_Get();
50   return s->default_pool;
51 }
52 
PyUpb_DescriptorPool_GetFileProtoDef(void)53 const upb_MessageDef* PyUpb_DescriptorPool_GetFileProtoDef(void) {
54   PyUpb_ModuleState* s = PyUpb_ModuleState_Get();
55   if (!s->c_descriptor_symtab) {
56     s->c_descriptor_symtab = upb_DefPool_New();
57   }
58   return google_protobuf_FileDescriptorProto_getmsgdef(s->c_descriptor_symtab);
59 }
60 
PyUpb_DescriptorPool_DoCreateWithCache(PyTypeObject * type,PyObject * db,PyUpb_WeakMap * obj_cache)61 static PyObject* PyUpb_DescriptorPool_DoCreateWithCache(
62     PyTypeObject* type, PyObject* db, PyUpb_WeakMap* obj_cache) {
63   PyUpb_DescriptorPool* pool = (void*)PyType_GenericAlloc(type, 0);
64   pool->symtab = upb_DefPool_New();
65   pool->db = db;
66   Py_XINCREF(pool->db);
67   PyUpb_WeakMap_Add(obj_cache, pool->symtab, &pool->ob_base);
68   return &pool->ob_base;
69 }
70 
PyUpb_DescriptorPool_DoCreate(PyTypeObject * type,PyObject * db)71 static PyObject* PyUpb_DescriptorPool_DoCreate(PyTypeObject* type,
72                                                PyObject* db) {
73   return PyUpb_DescriptorPool_DoCreateWithCache(type, db,
74                                                 PyUpb_ObjCache_Instance());
75 }
76 
PyUpb_DescriptorPool_GetSymtab(PyObject * pool)77 upb_DefPool* PyUpb_DescriptorPool_GetSymtab(PyObject* pool) {
78   return ((PyUpb_DescriptorPool*)pool)->symtab;
79 }
80 
PyUpb_DescriptorPool_Traverse(PyUpb_DescriptorPool * self,visitproc visit,void * arg)81 static int PyUpb_DescriptorPool_Traverse(PyUpb_DescriptorPool* self,
82                                          visitproc visit, void* arg) {
83   Py_VISIT(self->db);
84   return 0;
85 }
86 
PyUpb_DescriptorPool_Clear(PyUpb_DescriptorPool * self)87 static int PyUpb_DescriptorPool_Clear(PyUpb_DescriptorPool* self) {
88   Py_CLEAR(self->db);
89   return 0;
90 }
91 
PyUpb_DescriptorPool_Get(const upb_DefPool * symtab)92 PyObject* PyUpb_DescriptorPool_Get(const upb_DefPool* symtab) {
93   PyObject* pool = PyUpb_ObjCache_Get(symtab);
94   assert(pool);
95   return pool;
96 }
97 
PyUpb_DescriptorPool_Dealloc(PyUpb_DescriptorPool * self)98 static void PyUpb_DescriptorPool_Dealloc(PyUpb_DescriptorPool* self) {
99   PyUpb_DescriptorPool_Clear(self);
100   upb_DefPool_Free(self->symtab);
101   PyUpb_ObjCache_Delete(self->symtab);
102   PyUpb_Dealloc(self);
103 }
104 
105 /*
106  * DescriptorPool.__new__()
107  *
108  * Implements:
109  *   DescriptorPool(descriptor_db=None)
110  */
PyUpb_DescriptorPool_New(PyTypeObject * type,PyObject * args,PyObject * kwargs)111 static PyObject* PyUpb_DescriptorPool_New(PyTypeObject* type, PyObject* args,
112                                           PyObject* kwargs) {
113   char* kwlist[] = {"descriptor_db", 0};
114   PyObject* db = NULL;
115 
116   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &db)) {
117     return NULL;
118   }
119 
120   if (db == Py_None) db = NULL;
121   return PyUpb_DescriptorPool_DoCreate(type, db);
122 }
123 
124 static PyObject* PyUpb_DescriptorPool_DoAdd(PyObject* _self,
125                                             PyObject* file_desc);
126 
PyUpb_DescriptorPool_TryLoadFileProto(PyUpb_DescriptorPool * self,PyObject * proto)127 static bool PyUpb_DescriptorPool_TryLoadFileProto(PyUpb_DescriptorPool* self,
128                                                   PyObject* proto) {
129   if (proto == NULL) {
130     if (PyErr_ExceptionMatches(PyExc_KeyError)) {
131       // Expected error: item was simply not found.
132       PyErr_Clear();
133       return true;  // We didn't accomplish our goal, but we didn't error out.
134     }
135     return false;
136   }
137   if (proto == Py_None) return true;
138   PyObject* ret = PyUpb_DescriptorPool_DoAdd((PyObject*)self, proto);
139   bool ok = ret != NULL;
140   Py_XDECREF(ret);
141   return ok;
142 }
143 
PyUpb_DescriptorPool_TryLoadSymbol(PyUpb_DescriptorPool * self,PyObject * sym)144 static bool PyUpb_DescriptorPool_TryLoadSymbol(PyUpb_DescriptorPool* self,
145                                                PyObject* sym) {
146   if (!self->db) return false;
147   PyObject* file_proto =
148       PyObject_CallMethod(self->db, "FindFileContainingSymbol", "O", sym);
149   bool ret = PyUpb_DescriptorPool_TryLoadFileProto(self, file_proto);
150   Py_XDECREF(file_proto);
151   return ret;
152 }
153 
PyUpb_DescriptorPool_TryLoadFilename(PyUpb_DescriptorPool * self,PyObject * filename)154 static bool PyUpb_DescriptorPool_TryLoadFilename(PyUpb_DescriptorPool* self,
155                                                  PyObject* filename) {
156   if (!self->db) return false;
157   PyObject* file_proto =
158       PyObject_CallMethod(self->db, "FindFileByName", "O", filename);
159   bool ret = PyUpb_DescriptorPool_TryLoadFileProto(self, file_proto);
160   Py_XDECREF(file_proto);
161   return ret;
162 }
163 
PyUpb_DescriptorPool_CheckNoDatabase(PyObject * _self)164 bool PyUpb_DescriptorPool_CheckNoDatabase(PyObject* _self) { return true; }
165 
PyUpb_DescriptorPool_LoadDependentFiles(PyUpb_DescriptorPool * self,google_protobuf_FileDescriptorProto * proto)166 static bool PyUpb_DescriptorPool_LoadDependentFiles(
167     PyUpb_DescriptorPool* self, google_protobuf_FileDescriptorProto* proto) {
168   size_t n;
169   const upb_StringView* deps =
170       google_protobuf_FileDescriptorProto_dependency(proto, &n);
171   for (size_t i = 0; i < n; i++) {
172     const upb_FileDef* dep = upb_DefPool_FindFileByNameWithSize(
173         self->symtab, deps[i].data, deps[i].size);
174     if (!dep) {
175       PyObject* filename =
176           PyUnicode_FromStringAndSize(deps[i].data, deps[i].size);
177       if (!filename) return false;
178       bool ok = PyUpb_DescriptorPool_TryLoadFilename(self, filename);
179       Py_DECREF(filename);
180       if (!ok) return false;
181     }
182   }
183   return true;
184 }
185 
PyUpb_DescriptorPool_DoAddSerializedFile(PyObject * _self,PyObject * serialized_pb)186 static PyObject* PyUpb_DescriptorPool_DoAddSerializedFile(
187     PyObject* _self, PyObject* serialized_pb) {
188   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
189   upb_Arena* arena = upb_Arena_New();
190   if (!arena) PYUPB_RETURN_OOM;
191   PyObject* result = NULL;
192 
193   char* buf;
194   Py_ssize_t size;
195   if (PyBytes_AsStringAndSize(serialized_pb, &buf, &size) < 0) {
196     goto done;
197   }
198 
199   google_protobuf_FileDescriptorProto* proto =
200       google_protobuf_FileDescriptorProto_parse(buf, size, arena);
201   if (!proto) {
202     PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
203     goto done;
204   }
205 
206   upb_StringView name = google_protobuf_FileDescriptorProto_name(proto);
207   const upb_FileDef* file =
208       upb_DefPool_FindFileByNameWithSize(self->symtab, name.data, name.size);
209 
210   if (file) {
211     // If the existing file is equal to the new file, then silently ignore the
212     // duplicate add.
213     google_protobuf_FileDescriptorProto* existing =
214         upb_FileDef_ToProto(file, arena);
215     if (!existing) {
216       PyErr_SetNone(PyExc_MemoryError);
217       goto done;
218     }
219     const upb_MessageDef* m = PyUpb_DescriptorPool_GetFileProtoDef();
220     if (upb_Message_IsEqual(proto, existing, m)) {
221       result = PyUpb_FileDescriptor_Get(file);
222       goto done;
223     }
224   }
225 
226   if (self->db) {
227     if (!PyUpb_DescriptorPool_LoadDependentFiles(self, proto)) goto done;
228   }
229 
230   upb_Status status;
231   upb_Status_Clear(&status);
232 
233   const upb_FileDef* filedef =
234       upb_DefPool_AddFile(self->symtab, proto, &status);
235   if (!filedef) {
236     PyErr_Format(PyExc_TypeError,
237                  "Couldn't build proto file into descriptor pool: %s",
238                  upb_Status_ErrorMessage(&status));
239     goto done;
240   }
241 
242   result = PyUpb_FileDescriptor_Get(filedef);
243 
244 done:
245   upb_Arena_Free(arena);
246   return result;
247 }
248 
PyUpb_DescriptorPool_DoAdd(PyObject * _self,PyObject * file_desc)249 static PyObject* PyUpb_DescriptorPool_DoAdd(PyObject* _self,
250                                             PyObject* file_desc) {
251   if (!PyUpb_Message_Verify(file_desc)) return NULL;
252   const upb_MessageDef* m = PyUpb_Message_GetMsgdef(file_desc);
253   const char* file_proto_name =
254       PYUPB_DESCRIPTOR_PROTO_PACKAGE ".FileDescriptorProto";
255   if (strcmp(upb_MessageDef_FullName(m), file_proto_name) != 0) {
256     return PyErr_Format(PyExc_TypeError, "Can only add FileDescriptorProto");
257   }
258   PyObject* subargs = PyTuple_New(0);
259   if (!subargs) return NULL;
260   PyObject* serialized =
261       PyUpb_Message_SerializeToString(file_desc, subargs, NULL);
262   Py_DECREF(subargs);
263   if (!serialized) return NULL;
264   PyObject* ret = PyUpb_DescriptorPool_DoAddSerializedFile(_self, serialized);
265   Py_DECREF(serialized);
266   return ret;
267 }
268 
269 /*
270  * PyUpb_DescriptorPool_AddSerializedFile()
271  *
272  * Implements:
273  *   DescriptorPool.AddSerializedFile(self, serialized_file_descriptor)
274  *
275  * Adds the given serialized FileDescriptorProto to the pool.
276  */
PyUpb_DescriptorPool_AddSerializedFile(PyObject * _self,PyObject * serialized_pb)277 static PyObject* PyUpb_DescriptorPool_AddSerializedFile(
278     PyObject* _self, PyObject* serialized_pb) {
279   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
280   if (self->db) {
281     PyErr_SetString(
282         PyExc_ValueError,
283         "Cannot call AddSerializedFile on a DescriptorPool that uses a "
284         "DescriptorDatabase. Add your file to the underlying database.");
285     return false;
286   }
287   return PyUpb_DescriptorPool_DoAddSerializedFile(_self, serialized_pb);
288 }
289 
PyUpb_DescriptorPool_Add(PyObject * _self,PyObject * file_desc)290 static PyObject* PyUpb_DescriptorPool_Add(PyObject* _self,
291                                           PyObject* file_desc) {
292   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
293   if (self->db) {
294     PyErr_SetString(
295         PyExc_ValueError,
296         "Cannot call Add on a DescriptorPool that uses a DescriptorDatabase. "
297         "Add your file to the underlying database.");
298     return false;
299   }
300   return PyUpb_DescriptorPool_DoAdd(_self, file_desc);
301 }
302 
303 /*
304  * PyUpb_DescriptorPool_FindFileByName()
305  *
306  * Implements:
307  *   DescriptorPool.FindFileByName(self, name)
308  */
PyUpb_DescriptorPool_FindFileByName(PyObject * _self,PyObject * arg)309 static PyObject* PyUpb_DescriptorPool_FindFileByName(PyObject* _self,
310                                                      PyObject* arg) {
311   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
312 
313   const char* name = PyUpb_VerifyStrData(arg);
314   if (!name) return NULL;
315 
316   const upb_FileDef* file = upb_DefPool_FindFileByName(self->symtab, name);
317   if (file == NULL && self->db) {
318     if (!PyUpb_DescriptorPool_TryLoadFilename(self, arg)) return NULL;
319     file = upb_DefPool_FindFileByName(self->symtab, name);
320   }
321   if (file == NULL) {
322     return PyErr_Format(PyExc_KeyError, "Couldn't find file %.200s", name);
323   }
324 
325   return PyUpb_FileDescriptor_Get(file);
326 }
327 
328 /*
329  * PyUpb_DescriptorPool_FindExtensionByName()
330  *
331  * Implements:
332  *   DescriptorPool.FindExtensionByName(self, name)
333  */
PyUpb_DescriptorPool_FindExtensionByName(PyObject * _self,PyObject * arg)334 static PyObject* PyUpb_DescriptorPool_FindExtensionByName(PyObject* _self,
335                                                           PyObject* arg) {
336   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
337 
338   const char* name = PyUpb_VerifyStrData(arg);
339   if (!name) return NULL;
340 
341   const upb_FieldDef* field =
342       upb_DefPool_FindExtensionByName(self->symtab, name);
343   if (field == NULL && self->db) {
344     if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
345     field = upb_DefPool_FindExtensionByName(self->symtab, name);
346   }
347   if (field == NULL) {
348     return PyErr_Format(PyExc_KeyError, "Couldn't find extension %.200s", name);
349   }
350 
351   return PyUpb_FieldDescriptor_Get(field);
352 }
353 
354 /*
355  * PyUpb_DescriptorPool_FindMessageTypeByName()
356  *
357  * Implements:
358  *   DescriptorPool.FindMessageTypeByName(self, name)
359  */
PyUpb_DescriptorPool_FindMessageTypeByName(PyObject * _self,PyObject * arg)360 static PyObject* PyUpb_DescriptorPool_FindMessageTypeByName(PyObject* _self,
361                                                             PyObject* arg) {
362   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
363 
364   const char* name = PyUpb_VerifyStrData(arg);
365   if (!name) return NULL;
366 
367   const upb_MessageDef* m = upb_DefPool_FindMessageByName(self->symtab, name);
368   if (m == NULL && self->db) {
369     if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
370     m = upb_DefPool_FindMessageByName(self->symtab, name);
371   }
372   if (m == NULL) {
373     return PyErr_Format(PyExc_KeyError, "Couldn't find message %.200s", name);
374   }
375 
376   return PyUpb_Descriptor_Get(m);
377 }
378 
379 // Splits a dotted symbol like foo.bar.baz on the last dot.  Returns the portion
380 // after the last dot (baz) and updates `*parent_size` to the length of the
381 // parent (foo.bar).  Returns NULL if no dots were present.
PyUpb_DescriptorPool_SplitSymbolName(const char * sym,size_t * parent_size)382 static const char* PyUpb_DescriptorPool_SplitSymbolName(const char* sym,
383                                                         size_t* parent_size) {
384   const char* last_dot = strrchr(sym, '.');
385   if (!last_dot) return NULL;
386   *parent_size = last_dot - sym;
387   return last_dot + 1;
388 }
389 
390 /*
391  * PyUpb_DescriptorPool_FindFieldByName()
392  *
393  * Implements:
394  *   DescriptorPool.FindFieldByName(self, name)
395  */
PyUpb_DescriptorPool_FindFieldByName(PyObject * _self,PyObject * arg)396 static PyObject* PyUpb_DescriptorPool_FindFieldByName(PyObject* _self,
397                                                       PyObject* arg) {
398   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
399 
400   const char* name = PyUpb_VerifyStrData(arg);
401   if (!name) return NULL;
402 
403   size_t parent_size;
404   const char* child = PyUpb_DescriptorPool_SplitSymbolName(name, &parent_size);
405   const upb_FieldDef* f = NULL;
406   if (child) {
407     const upb_MessageDef* parent =
408         upb_DefPool_FindMessageByNameWithSize(self->symtab, name, parent_size);
409     if (parent == NULL && self->db) {
410       if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
411       parent = upb_DefPool_FindMessageByNameWithSize(self->symtab, name,
412                                                      parent_size);
413     }
414     if (parent) {
415       f = upb_MessageDef_FindFieldByName(parent, child);
416     }
417   }
418 
419   if (!f) {
420     return PyErr_Format(PyExc_KeyError, "Couldn't find message %.200s", name);
421   }
422 
423   return PyUpb_FieldDescriptor_Get(f);
424 }
425 
426 /*
427  * PyUpb_DescriptorPool_FindEnumTypeByName()
428  *
429  * Implements:
430  *   DescriptorPool.FindEnumTypeByName(self, name)
431  */
PyUpb_DescriptorPool_FindEnumTypeByName(PyObject * _self,PyObject * arg)432 static PyObject* PyUpb_DescriptorPool_FindEnumTypeByName(PyObject* _self,
433                                                          PyObject* arg) {
434   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
435 
436   const char* name = PyUpb_VerifyStrData(arg);
437   if (!name) return NULL;
438 
439   const upb_EnumDef* e = upb_DefPool_FindEnumByName(self->symtab, name);
440   if (e == NULL && self->db) {
441     if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
442     e = upb_DefPool_FindEnumByName(self->symtab, name);
443   }
444   if (e == NULL) {
445     return PyErr_Format(PyExc_KeyError, "Couldn't find enum %.200s", name);
446   }
447 
448   return PyUpb_EnumDescriptor_Get(e);
449 }
450 
451 /*
452  * PyUpb_DescriptorPool_FindOneofByName()
453  *
454  * Implements:
455  *   DescriptorPool.FindOneofByName(self, name)
456  */
PyUpb_DescriptorPool_FindOneofByName(PyObject * _self,PyObject * arg)457 static PyObject* PyUpb_DescriptorPool_FindOneofByName(PyObject* _self,
458                                                       PyObject* arg) {
459   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
460 
461   const char* name = PyUpb_VerifyStrData(arg);
462   if (!name) return NULL;
463 
464   size_t parent_size;
465   const char* child = PyUpb_DescriptorPool_SplitSymbolName(name, &parent_size);
466 
467   if (child) {
468     const upb_MessageDef* parent =
469         upb_DefPool_FindMessageByNameWithSize(self->symtab, name, parent_size);
470     if (parent == NULL && self->db) {
471       if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
472       parent = upb_DefPool_FindMessageByNameWithSize(self->symtab, name,
473                                                      parent_size);
474     }
475     if (parent) {
476       const upb_OneofDef* o = upb_MessageDef_FindOneofByName(parent, child);
477       return PyUpb_OneofDescriptor_Get(o);
478     }
479   }
480 
481   return PyErr_Format(PyExc_KeyError, "Couldn't find oneof %.200s", name);
482 }
483 
PyUpb_DescriptorPool_FindServiceByName(PyObject * _self,PyObject * arg)484 static PyObject* PyUpb_DescriptorPool_FindServiceByName(PyObject* _self,
485                                                         PyObject* arg) {
486   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
487 
488   const char* name = PyUpb_VerifyStrData(arg);
489   if (!name) return NULL;
490 
491   const upb_ServiceDef* s = upb_DefPool_FindServiceByName(self->symtab, name);
492   if (s == NULL && self->db) {
493     if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
494     s = upb_DefPool_FindServiceByName(self->symtab, name);
495   }
496   if (s == NULL) {
497     return PyErr_Format(PyExc_KeyError, "Couldn't find service %.200s", name);
498   }
499 
500   return PyUpb_ServiceDescriptor_Get(s);
501 }
502 
PyUpb_DescriptorPool_FindMethodByName(PyObject * _self,PyObject * arg)503 static PyObject* PyUpb_DescriptorPool_FindMethodByName(PyObject* _self,
504                                                        PyObject* arg) {
505   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
506 
507   const char* name = PyUpb_VerifyStrData(arg);
508   if (!name) return NULL;
509   size_t parent_size;
510   const char* child = PyUpb_DescriptorPool_SplitSymbolName(name, &parent_size);
511 
512   if (!child) goto err;
513   const upb_ServiceDef* parent =
514       upb_DefPool_FindServiceByNameWithSize(self->symtab, name, parent_size);
515   if (parent == NULL && self->db) {
516     if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
517     parent =
518         upb_DefPool_FindServiceByNameWithSize(self->symtab, name, parent_size);
519   }
520   if (!parent) goto err;
521   const upb_MethodDef* m = upb_ServiceDef_FindMethodByName(parent, child);
522   if (!m) goto err;
523   return PyUpb_MethodDescriptor_Get(m);
524 
525 err:
526   return PyErr_Format(PyExc_KeyError, "Couldn't find method %.200s", name);
527 }
528 
PyUpb_DescriptorPool_FindFileContainingSymbol(PyObject * _self,PyObject * arg)529 static PyObject* PyUpb_DescriptorPool_FindFileContainingSymbol(PyObject* _self,
530                                                                PyObject* arg) {
531   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
532 
533   const char* name = PyUpb_VerifyStrData(arg);
534   if (!name) return NULL;
535 
536   const upb_FileDef* f =
537       upb_DefPool_FindFileContainingSymbol(self->symtab, name);
538   if (f == NULL && self->db) {
539     if (!PyUpb_DescriptorPool_TryLoadSymbol(self, arg)) return NULL;
540     f = upb_DefPool_FindFileContainingSymbol(self->symtab, name);
541   }
542   if (f == NULL) {
543     return PyErr_Format(PyExc_KeyError, "Couldn't find symbol %.200s", name);
544   }
545 
546   return PyUpb_FileDescriptor_Get(f);
547 }
548 
PyUpb_DescriptorPool_FindExtensionByNumber(PyObject * _self,PyObject * args)549 static PyObject* PyUpb_DescriptorPool_FindExtensionByNumber(PyObject* _self,
550                                                             PyObject* args) {
551   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
552   PyObject* message_descriptor;
553   int number;
554   if (!PyArg_ParseTuple(args, "Oi", &message_descriptor, &number)) {
555     return NULL;
556   }
557 
558   const upb_FieldDef* f = upb_DefPool_FindExtensionByNumber(
559       self->symtab, PyUpb_Descriptor_GetDef(message_descriptor), number);
560   if (f == NULL) {
561     return PyErr_Format(PyExc_KeyError, "Couldn't find Extension %d", number);
562   }
563 
564   return PyUpb_FieldDescriptor_Get(f);
565 }
566 
PyUpb_DescriptorPool_FindAllExtensions(PyObject * _self,PyObject * msg_desc)567 static PyObject* PyUpb_DescriptorPool_FindAllExtensions(PyObject* _self,
568                                                         PyObject* msg_desc) {
569   PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
570   const upb_MessageDef* m = PyUpb_Descriptor_GetDef(msg_desc);
571   size_t n;
572   const upb_FieldDef** ext = upb_DefPool_GetAllExtensions(self->symtab, m, &n);
573   PyObject* ret = PyList_New(n);
574   if (!ret) goto done;
575   for (size_t i = 0; i < n; i++) {
576     PyObject* field = PyUpb_FieldDescriptor_Get(ext[i]);
577     if (!field) {
578       Py_DECREF(ret);
579       ret = NULL;
580       goto done;
581     }
582     PyList_SetItem(ret, i, field);
583   }
584 done:
585   free(ext);
586   return ret;
587 }
588 
589 static PyMethodDef PyUpb_DescriptorPool_Methods[] = {
590     {"Add", PyUpb_DescriptorPool_Add, METH_O,
591      "Adds the FileDescriptorProto and its types to this pool."},
592     {"AddSerializedFile", PyUpb_DescriptorPool_AddSerializedFile, METH_O,
593      "Adds a serialized FileDescriptorProto to this pool."},
594     {"FindFileByName", PyUpb_DescriptorPool_FindFileByName, METH_O,
595      "Searches for a file descriptor by its .proto name."},
596     {"FindMessageTypeByName", PyUpb_DescriptorPool_FindMessageTypeByName,
597      METH_O, "Searches for a message descriptor by full name."},
598     {"FindFieldByName", PyUpb_DescriptorPool_FindFieldByName, METH_O,
599      "Searches for a field descriptor by full name."},
600     {"FindExtensionByName", PyUpb_DescriptorPool_FindExtensionByName, METH_O,
601      "Searches for extension descriptor by full name."},
602     {"FindEnumTypeByName", PyUpb_DescriptorPool_FindEnumTypeByName, METH_O,
603      "Searches for enum type descriptor by full name."},
604     {"FindOneofByName", PyUpb_DescriptorPool_FindOneofByName, METH_O,
605      "Searches for oneof descriptor by full name."},
606     {"FindServiceByName", PyUpb_DescriptorPool_FindServiceByName, METH_O,
607      "Searches for service descriptor by full name."},
608     {"FindMethodByName", PyUpb_DescriptorPool_FindMethodByName, METH_O,
609      "Searches for method descriptor by full name."},
610     {"FindFileContainingSymbol", PyUpb_DescriptorPool_FindFileContainingSymbol,
611      METH_O, "Gets the FileDescriptor containing the specified symbol."},
612     {"FindExtensionByNumber", PyUpb_DescriptorPool_FindExtensionByNumber,
613      METH_VARARGS, "Gets the extension descriptor for the given number."},
614     {"FindAllExtensions", PyUpb_DescriptorPool_FindAllExtensions, METH_O,
615      "Gets all known extensions of the given message descriptor."},
616     {NULL}};
617 
618 static PyType_Slot PyUpb_DescriptorPool_Slots[] = {
619     {Py_tp_clear, PyUpb_DescriptorPool_Clear},
620     {Py_tp_dealloc, PyUpb_DescriptorPool_Dealloc},
621     {Py_tp_methods, PyUpb_DescriptorPool_Methods},
622     {Py_tp_new, PyUpb_DescriptorPool_New},
623     {Py_tp_traverse, PyUpb_DescriptorPool_Traverse},
624     {0, NULL}};
625 
626 static PyType_Spec PyUpb_DescriptorPool_Spec = {
627     PYUPB_MODULE_NAME ".DescriptorPool",
628     sizeof(PyUpb_DescriptorPool),
629     0,  // tp_itemsize
630     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
631     PyUpb_DescriptorPool_Slots,
632 };
633 
634 // -----------------------------------------------------------------------------
635 // Top Level
636 // -----------------------------------------------------------------------------
637 
PyUpb_InitDescriptorPool(PyObject * m)638 bool PyUpb_InitDescriptorPool(PyObject* m) {
639   PyUpb_ModuleState* state = PyUpb_ModuleState_GetFromModule(m);
640   PyTypeObject* descriptor_pool_type =
641       PyUpb_AddClass(m, &PyUpb_DescriptorPool_Spec);
642 
643   if (!descriptor_pool_type) return false;
644 
645   state->default_pool = PyUpb_DescriptorPool_DoCreateWithCache(
646       descriptor_pool_type, NULL, state->obj_cache);
647   return state->default_pool &&
648          PyModule_AddObject(m, "default_pool", state->default_pool) == 0;
649 }
650