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 "upb/hash/int_table.h"
29 #include "upb/hash/str_table.h"
30 #include "upb/reflection/def_builder_internal.h"
31 #include "upb/reflection/def_pool_internal.h"
32 #include "upb/reflection/def_type.h"
33 #include "upb/reflection/enum_def_internal.h"
34 #include "upb/reflection/enum_value_def_internal.h"
35 #include "upb/reflection/field_def_internal.h"
36 #include "upb/reflection/file_def_internal.h"
37 #include "upb/reflection/message_def_internal.h"
38 #include "upb/reflection/service_def_internal.h"
39
40 // Must be last.
41 #include "upb/port/def.inc"
42
43 struct upb_DefPool {
44 upb_Arena* arena;
45 upb_strtable syms; // full_name -> packed def ptr
46 upb_strtable files; // file_name -> (upb_FileDef*)
47 upb_inttable exts; // (upb_MiniTableExtension*) -> (upb_FieldDef*)
48 upb_ExtensionRegistry* extreg;
49 upb_MiniTablePlatform platform;
50 void* scratch_data;
51 size_t scratch_size;
52 size_t bytes_loaded;
53 };
54
upb_DefPool_Free(upb_DefPool * s)55 void upb_DefPool_Free(upb_DefPool* s) {
56 upb_Arena_Free(s->arena);
57 upb_gfree(s->scratch_data);
58 upb_gfree(s);
59 }
60
upb_DefPool_New(void)61 upb_DefPool* upb_DefPool_New(void) {
62 upb_DefPool* s = upb_gmalloc(sizeof(*s));
63 if (!s) return NULL;
64
65 s->arena = upb_Arena_New();
66 s->bytes_loaded = 0;
67
68 s->scratch_size = 240;
69 s->scratch_data = upb_gmalloc(s->scratch_size);
70 if (!s->scratch_data) goto err;
71
72 if (!upb_strtable_init(&s->syms, 32, s->arena)) goto err;
73 if (!upb_strtable_init(&s->files, 4, s->arena)) goto err;
74 if (!upb_inttable_init(&s->exts, s->arena)) goto err;
75
76 s->extreg = upb_ExtensionRegistry_New(s->arena);
77 if (!s->extreg) goto err;
78
79 s->platform = kUpb_MiniTablePlatform_Native;
80
81 return s;
82
83 err:
84 upb_DefPool_Free(s);
85 return NULL;
86 }
87
_upb_DefPool_InsertExt(upb_DefPool * s,const upb_MiniTableExtension * ext,const upb_FieldDef * f)88 bool _upb_DefPool_InsertExt(upb_DefPool* s, const upb_MiniTableExtension* ext,
89 const upb_FieldDef* f) {
90 return upb_inttable_insert(&s->exts, (uintptr_t)ext, upb_value_constptr(f),
91 s->arena);
92 }
93
_upb_DefPool_InsertSym(upb_DefPool * s,upb_StringView sym,upb_value v,upb_Status * status)94 bool _upb_DefPool_InsertSym(upb_DefPool* s, upb_StringView sym, upb_value v,
95 upb_Status* status) {
96 // TODO: table should support an operation "tryinsert" to avoid the double
97 // lookup.
98 if (upb_strtable_lookup2(&s->syms, sym.data, sym.size, NULL)) {
99 upb_Status_SetErrorFormat(status, "duplicate symbol '%s'", sym.data);
100 return false;
101 }
102 if (!upb_strtable_insert(&s->syms, sym.data, sym.size, v, s->arena)) {
103 upb_Status_SetErrorMessage(status, "out of memory");
104 return false;
105 }
106 return true;
107 }
108
_upb_DefPool_Unpack(const upb_DefPool * s,const char * sym,size_t size,upb_deftype_t type)109 static const void* _upb_DefPool_Unpack(const upb_DefPool* s, const char* sym,
110 size_t size, upb_deftype_t type) {
111 upb_value v;
112 return upb_strtable_lookup2(&s->syms, sym, size, &v)
113 ? _upb_DefType_Unpack(v, type)
114 : NULL;
115 }
116
_upb_DefPool_LookupSym(const upb_DefPool * s,const char * sym,size_t size,upb_value * v)117 bool _upb_DefPool_LookupSym(const upb_DefPool* s, const char* sym, size_t size,
118 upb_value* v) {
119 return upb_strtable_lookup2(&s->syms, sym, size, v);
120 }
121
_upb_DefPool_ExtReg(const upb_DefPool * s)122 upb_ExtensionRegistry* _upb_DefPool_ExtReg(const upb_DefPool* s) {
123 return s->extreg;
124 }
125
_upb_DefPool_ScratchData(const upb_DefPool * s)126 void** _upb_DefPool_ScratchData(const upb_DefPool* s) {
127 return (void**)&s->scratch_data;
128 }
129
_upb_DefPool_ScratchSize(const upb_DefPool * s)130 size_t* _upb_DefPool_ScratchSize(const upb_DefPool* s) {
131 return (size_t*)&s->scratch_size;
132 }
133
_upb_DefPool_SetPlatform(upb_DefPool * s,upb_MiniTablePlatform platform)134 void _upb_DefPool_SetPlatform(upb_DefPool* s, upb_MiniTablePlatform platform) {
135 assert(upb_strtable_count(&s->files) == 0);
136 s->platform = platform;
137 }
138
upb_DefPool_FindMessageByName(const upb_DefPool * s,const char * sym)139 const upb_MessageDef* upb_DefPool_FindMessageByName(const upb_DefPool* s,
140 const char* sym) {
141 return _upb_DefPool_Unpack(s, sym, strlen(sym), UPB_DEFTYPE_MSG);
142 }
143
upb_DefPool_FindMessageByNameWithSize(const upb_DefPool * s,const char * sym,size_t len)144 const upb_MessageDef* upb_DefPool_FindMessageByNameWithSize(
145 const upb_DefPool* s, const char* sym, size_t len) {
146 return _upb_DefPool_Unpack(s, sym, len, UPB_DEFTYPE_MSG);
147 }
148
upb_DefPool_FindEnumByName(const upb_DefPool * s,const char * sym)149 const upb_EnumDef* upb_DefPool_FindEnumByName(const upb_DefPool* s,
150 const char* sym) {
151 return _upb_DefPool_Unpack(s, sym, strlen(sym), UPB_DEFTYPE_ENUM);
152 }
153
upb_DefPool_FindEnumByNameval(const upb_DefPool * s,const char * sym)154 const upb_EnumValueDef* upb_DefPool_FindEnumByNameval(const upb_DefPool* s,
155 const char* sym) {
156 return _upb_DefPool_Unpack(s, sym, strlen(sym), UPB_DEFTYPE_ENUMVAL);
157 }
158
upb_DefPool_FindFileByName(const upb_DefPool * s,const char * name)159 const upb_FileDef* upb_DefPool_FindFileByName(const upb_DefPool* s,
160 const char* name) {
161 upb_value v;
162 return upb_strtable_lookup(&s->files, name, &v) ? upb_value_getconstptr(v)
163 : NULL;
164 }
165
upb_DefPool_FindFileByNameWithSize(const upb_DefPool * s,const char * name,size_t len)166 const upb_FileDef* upb_DefPool_FindFileByNameWithSize(const upb_DefPool* s,
167 const char* name,
168 size_t len) {
169 upb_value v;
170 return upb_strtable_lookup2(&s->files, name, len, &v)
171 ? upb_value_getconstptr(v)
172 : NULL;
173 }
174
upb_DefPool_FindExtensionByNameWithSize(const upb_DefPool * s,const char * name,size_t size)175 const upb_FieldDef* upb_DefPool_FindExtensionByNameWithSize(
176 const upb_DefPool* s, const char* name, size_t size) {
177 upb_value v;
178 if (!upb_strtable_lookup2(&s->syms, name, size, &v)) return NULL;
179
180 switch (_upb_DefType_Type(v)) {
181 case UPB_DEFTYPE_FIELD:
182 return _upb_DefType_Unpack(v, UPB_DEFTYPE_FIELD);
183 case UPB_DEFTYPE_MSG: {
184 const upb_MessageDef* m = _upb_DefType_Unpack(v, UPB_DEFTYPE_MSG);
185 return _upb_MessageDef_InMessageSet(m)
186 ? upb_MessageDef_NestedExtension(m, 0)
187 : NULL;
188 }
189 default:
190 break;
191 }
192
193 return NULL;
194 }
195
upb_DefPool_FindExtensionByName(const upb_DefPool * s,const char * sym)196 const upb_FieldDef* upb_DefPool_FindExtensionByName(const upb_DefPool* s,
197 const char* sym) {
198 return upb_DefPool_FindExtensionByNameWithSize(s, sym, strlen(sym));
199 }
200
upb_DefPool_FindServiceByName(const upb_DefPool * s,const char * name)201 const upb_ServiceDef* upb_DefPool_FindServiceByName(const upb_DefPool* s,
202 const char* name) {
203 return _upb_DefPool_Unpack(s, name, strlen(name), UPB_DEFTYPE_SERVICE);
204 }
205
upb_DefPool_FindServiceByNameWithSize(const upb_DefPool * s,const char * name,size_t size)206 const upb_ServiceDef* upb_DefPool_FindServiceByNameWithSize(
207 const upb_DefPool* s, const char* name, size_t size) {
208 return _upb_DefPool_Unpack(s, name, size, UPB_DEFTYPE_SERVICE);
209 }
210
upb_DefPool_FindFileContainingSymbol(const upb_DefPool * s,const char * name)211 const upb_FileDef* upb_DefPool_FindFileContainingSymbol(const upb_DefPool* s,
212 const char* name) {
213 upb_value v;
214 // TODO(haberman): non-extension fields and oneofs.
215 if (upb_strtable_lookup(&s->syms, name, &v)) {
216 switch (_upb_DefType_Type(v)) {
217 case UPB_DEFTYPE_EXT: {
218 const upb_FieldDef* f = _upb_DefType_Unpack(v, UPB_DEFTYPE_EXT);
219 return upb_FieldDef_File(f);
220 }
221 case UPB_DEFTYPE_MSG: {
222 const upb_MessageDef* m = _upb_DefType_Unpack(v, UPB_DEFTYPE_MSG);
223 return upb_MessageDef_File(m);
224 }
225 case UPB_DEFTYPE_ENUM: {
226 const upb_EnumDef* e = _upb_DefType_Unpack(v, UPB_DEFTYPE_ENUM);
227 return upb_EnumDef_File(e);
228 }
229 case UPB_DEFTYPE_ENUMVAL: {
230 const upb_EnumValueDef* ev =
231 _upb_DefType_Unpack(v, UPB_DEFTYPE_ENUMVAL);
232 return upb_EnumDef_File(upb_EnumValueDef_Enum(ev));
233 }
234 case UPB_DEFTYPE_SERVICE: {
235 const upb_ServiceDef* service =
236 _upb_DefType_Unpack(v, UPB_DEFTYPE_SERVICE);
237 return upb_ServiceDef_File(service);
238 }
239 default:
240 UPB_UNREACHABLE();
241 }
242 }
243
244 const char* last_dot = strrchr(name, '.');
245 if (last_dot) {
246 const upb_MessageDef* parent =
247 upb_DefPool_FindMessageByNameWithSize(s, name, last_dot - name);
248 if (parent) {
249 const char* shortname = last_dot + 1;
250 if (upb_MessageDef_FindByNameWithSize(parent, shortname,
251 strlen(shortname), NULL, NULL)) {
252 return upb_MessageDef_File(parent);
253 }
254 }
255 }
256
257 return NULL;
258 }
259
remove_filedef(upb_DefPool * s,upb_FileDef * file)260 static void remove_filedef(upb_DefPool* s, upb_FileDef* file) {
261 intptr_t iter = UPB_INTTABLE_BEGIN;
262 upb_StringView key;
263 upb_value val;
264 while (upb_strtable_next2(&s->syms, &key, &val, &iter)) {
265 const upb_FileDef* f;
266 switch (_upb_DefType_Type(val)) {
267 case UPB_DEFTYPE_EXT:
268 f = upb_FieldDef_File(_upb_DefType_Unpack(val, UPB_DEFTYPE_EXT));
269 break;
270 case UPB_DEFTYPE_MSG:
271 f = upb_MessageDef_File(_upb_DefType_Unpack(val, UPB_DEFTYPE_MSG));
272 break;
273 case UPB_DEFTYPE_ENUM:
274 f = upb_EnumDef_File(_upb_DefType_Unpack(val, UPB_DEFTYPE_ENUM));
275 break;
276 case UPB_DEFTYPE_ENUMVAL:
277 f = upb_EnumDef_File(upb_EnumValueDef_Enum(
278 _upb_DefType_Unpack(val, UPB_DEFTYPE_ENUMVAL)));
279 break;
280 case UPB_DEFTYPE_SERVICE:
281 f = upb_ServiceDef_File(_upb_DefType_Unpack(val, UPB_DEFTYPE_SERVICE));
282 break;
283 default:
284 UPB_UNREACHABLE();
285 }
286
287 if (f == file) upb_strtable_removeiter(&s->syms, &iter);
288 }
289 }
290
upb_DefBuilder_AddFileToPool(upb_DefBuilder * const builder,upb_DefPool * const s,const UPB_DESC (FileDescriptorProto)* const file_proto,const upb_StringView name,upb_Status * const status)291 static const upb_FileDef* upb_DefBuilder_AddFileToPool(
292 upb_DefBuilder* const builder, upb_DefPool* const s,
293 const UPB_DESC(FileDescriptorProto) * const file_proto,
294 const upb_StringView name, upb_Status* const status) {
295 if (UPB_SETJMP(builder->err) != 0) {
296 UPB_ASSERT(!upb_Status_IsOk(status));
297 if (builder->file) {
298 remove_filedef(s, builder->file);
299 builder->file = NULL;
300 }
301 } else if (!builder->arena || !builder->tmp_arena) {
302 _upb_DefBuilder_OomErr(builder);
303 } else {
304 _upb_FileDef_Create(builder, file_proto);
305 upb_strtable_insert(&s->files, name.data, name.size,
306 upb_value_constptr(builder->file), builder->arena);
307 UPB_ASSERT(upb_Status_IsOk(status));
308 upb_Arena_Fuse(s->arena, builder->arena);
309 }
310
311 if (builder->arena) upb_Arena_Free(builder->arena);
312 if (builder->tmp_arena) upb_Arena_Free(builder->tmp_arena);
313 return builder->file;
314 }
315
_upb_DefPool_AddFile(upb_DefPool * s,const UPB_DESC (FileDescriptorProto)* file_proto,const upb_MiniTableFile * layout,upb_Status * status)316 static const upb_FileDef* _upb_DefPool_AddFile(
317 upb_DefPool* s, const UPB_DESC(FileDescriptorProto) * file_proto,
318 const upb_MiniTableFile* layout, upb_Status* status) {
319 const upb_StringView name = UPB_DESC(FileDescriptorProto_name)(file_proto);
320
321 // Determine whether we already know about this file.
322 {
323 upb_value v;
324 if (upb_strtable_lookup2(&s->files, name.data, name.size, &v)) {
325 upb_Status_SetErrorFormat(status,
326 "duplicate file name " UPB_STRINGVIEW_FORMAT,
327 UPB_STRINGVIEW_ARGS(name));
328 return NULL;
329 }
330 }
331
332 upb_DefBuilder ctx = {
333 .symtab = s,
334 .layout = layout,
335 .platform = s->platform,
336 .msg_count = 0,
337 .enum_count = 0,
338 .ext_count = 0,
339 .status = status,
340 .file = NULL,
341 .arena = upb_Arena_New(),
342 .tmp_arena = upb_Arena_New(),
343 };
344
345 return upb_DefBuilder_AddFileToPool(&ctx, s, file_proto, name, status);
346 }
347
upb_DefPool_AddFile(upb_DefPool * s,const UPB_DESC (FileDescriptorProto)* file_proto,upb_Status * status)348 const upb_FileDef* upb_DefPool_AddFile(upb_DefPool* s,
349 const UPB_DESC(FileDescriptorProto) *
350 file_proto,
351 upb_Status* status) {
352 return _upb_DefPool_AddFile(s, file_proto, NULL, status);
353 }
354
_upb_DefPool_LoadDefInitEx(upb_DefPool * s,const _upb_DefPool_Init * init,bool rebuild_minitable)355 bool _upb_DefPool_LoadDefInitEx(upb_DefPool* s, const _upb_DefPool_Init* init,
356 bool rebuild_minitable) {
357 /* Since this function should never fail (it would indicate a bug in upb) we
358 * print errors to stderr instead of returning error status to the user. */
359 _upb_DefPool_Init** deps = init->deps;
360 UPB_DESC(FileDescriptorProto) * file;
361 upb_Arena* arena;
362 upb_Status status;
363
364 upb_Status_Clear(&status);
365
366 if (upb_DefPool_FindFileByName(s, init->filename)) {
367 return true;
368 }
369
370 arena = upb_Arena_New();
371
372 for (; *deps; deps++) {
373 if (!_upb_DefPool_LoadDefInitEx(s, *deps, rebuild_minitable)) goto err;
374 }
375
376 file = UPB_DESC(FileDescriptorProto_parse_ex)(
377 init->descriptor.data, init->descriptor.size, NULL,
378 kUpb_DecodeOption_AliasString, arena);
379 s->bytes_loaded += init->descriptor.size;
380
381 if (!file) {
382 upb_Status_SetErrorFormat(
383 &status,
384 "Failed to parse compiled-in descriptor for file '%s'. This should "
385 "never happen.",
386 init->filename);
387 goto err;
388 }
389
390 const upb_MiniTableFile* mt = rebuild_minitable ? NULL : init->layout;
391 if (!_upb_DefPool_AddFile(s, file, mt, &status)) {
392 goto err;
393 }
394
395 upb_Arena_Free(arena);
396 return true;
397
398 err:
399 fprintf(stderr,
400 "Error loading compiled-in descriptor for file '%s' (this should "
401 "never happen): %s\n",
402 init->filename, upb_Status_ErrorMessage(&status));
403 upb_Arena_Free(arena);
404 return false;
405 }
406
_upb_DefPool_BytesLoaded(const upb_DefPool * s)407 size_t _upb_DefPool_BytesLoaded(const upb_DefPool* s) {
408 return s->bytes_loaded;
409 }
410
_upb_DefPool_Arena(const upb_DefPool * s)411 upb_Arena* _upb_DefPool_Arena(const upb_DefPool* s) { return s->arena; }
412
upb_DefPool_FindExtensionByMiniTable(const upb_DefPool * s,const upb_MiniTableExtension * ext)413 const upb_FieldDef* upb_DefPool_FindExtensionByMiniTable(
414 const upb_DefPool* s, const upb_MiniTableExtension* ext) {
415 upb_value v;
416 bool ok = upb_inttable_lookup(&s->exts, (uintptr_t)ext, &v);
417 UPB_ASSERT(ok);
418 return upb_value_getconstptr(v);
419 }
420
upb_DefPool_FindExtensionByNumber(const upb_DefPool * s,const upb_MessageDef * m,int32_t fieldnum)421 const upb_FieldDef* upb_DefPool_FindExtensionByNumber(const upb_DefPool* s,
422 const upb_MessageDef* m,
423 int32_t fieldnum) {
424 const upb_MiniTable* t = upb_MessageDef_MiniTable(m);
425 const upb_MiniTableExtension* ext =
426 upb_ExtensionRegistry_Lookup(s->extreg, t, fieldnum);
427 return ext ? upb_DefPool_FindExtensionByMiniTable(s, ext) : NULL;
428 }
429
upb_DefPool_ExtensionRegistry(const upb_DefPool * s)430 const upb_ExtensionRegistry* upb_DefPool_ExtensionRegistry(
431 const upb_DefPool* s) {
432 return s->extreg;
433 }
434
upb_DefPool_GetAllExtensions(const upb_DefPool * s,const upb_MessageDef * m,size_t * count)435 const upb_FieldDef** upb_DefPool_GetAllExtensions(const upb_DefPool* s,
436 const upb_MessageDef* m,
437 size_t* count) {
438 size_t n = 0;
439 intptr_t iter = UPB_INTTABLE_BEGIN;
440 uintptr_t key;
441 upb_value val;
442 // This is O(all exts) instead of O(exts for m). If we need this to be
443 // efficient we may need to make extreg into a two-level table, or have a
444 // second per-message index.
445 while (upb_inttable_next(&s->exts, &key, &val, &iter)) {
446 const upb_FieldDef* f = upb_value_getconstptr(val);
447 if (upb_FieldDef_ContainingType(f) == m) n++;
448 }
449 const upb_FieldDef** exts = malloc(n * sizeof(*exts));
450 iter = UPB_INTTABLE_BEGIN;
451 size_t i = 0;
452 while (upb_inttable_next(&s->exts, &key, &val, &iter)) {
453 const upb_FieldDef* f = upb_value_getconstptr(val);
454 if (upb_FieldDef_ContainingType(f) == m) exts[i++] = f;
455 }
456 *count = n;
457 return exts;
458 }
459
_upb_DefPool_LoadDefInit(upb_DefPool * s,const _upb_DefPool_Init * init)460 bool _upb_DefPool_LoadDefInit(upb_DefPool* s, const _upb_DefPool_Init* init) {
461 return _upb_DefPool_LoadDefInitEx(s, init, false);
462 }
463