1 // Copyright (c) 2009-2021, Google LLC
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name of Google LLC nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 // ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
19 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 #include <algorithm>
27 #include <cmath>
28 #include <cstddef>
29 #include <cstdint>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <limits>
33 #include <map>
34 #include <memory>
35 #include <string>
36 #include <utility>
37 #include <vector>
38
39 #include "absl/container/flat_hash_map.h"
40 #include "absl/container/flat_hash_set.h"
41 #include "absl/log/absl_check.h"
42 #include "absl/log/absl_log.h"
43 #include "absl/strings/escaping.h"
44 #include "absl/strings/string_view.h"
45 #include "absl/strings/substitute.h"
46 #include "upb/base/descriptor_constants.h"
47 #include "upb/base/string_view.h"
48 #include "upb/reflection/def.hpp"
49 #include "upb/wire/types.h"
50 #include "upbc/common.h"
51 #include "upbc/file_layout.h"
52 #include "upbc/names.h"
53 #include "upbc/plugin.h"
54
55 // Must be last.
56 #include "upb/port/def.inc"
57
58 namespace upbc {
59 namespace {
60
61 struct Options {
62 bool bootstrap = false;
63 };
64
65 // Returns fields in order of "hotness", eg. how frequently they appear in
66 // serialized payloads. Ideally this will use a profile. When we don't have
67 // that, we assume that fields with smaller numbers are used more frequently.
FieldHotnessOrder(upb::MessageDefPtr message)68 inline std::vector<upb::FieldDefPtr> FieldHotnessOrder(
69 upb::MessageDefPtr message) {
70 std::vector<upb::FieldDefPtr> fields;
71 size_t field_count = message.field_count();
72 fields.reserve(field_count);
73 for (size_t i = 0; i < field_count; i++) {
74 fields.push_back(message.field(i));
75 }
76 std::sort(fields.begin(), fields.end(),
77 [](upb::FieldDefPtr a, upb::FieldDefPtr b) {
78 return std::make_pair(!a.is_required(), a.number()) <
79 std::make_pair(!b.is_required(), b.number());
80 });
81 return fields;
82 }
83
SourceFilename(upb::FileDefPtr file)84 std::string SourceFilename(upb::FileDefPtr file) {
85 return StripExtension(file.name()) + ".upb.c";
86 }
87
MessageInitName(upb::MessageDefPtr descriptor)88 std::string MessageInitName(upb::MessageDefPtr descriptor) {
89 return absl::StrCat(MessageName(descriptor), "_msg_init");
90 }
91
MessageMiniTableRef(upb::MessageDefPtr descriptor,const Options & options)92 std::string MessageMiniTableRef(upb::MessageDefPtr descriptor,
93 const Options& options) {
94 if (options.bootstrap) {
95 return absl::StrCat(MessageInitName(descriptor), "()");
96 } else {
97 return absl::StrCat("&", MessageInitName(descriptor));
98 }
99 }
100
EnumInitName(upb::EnumDefPtr descriptor)101 std::string EnumInitName(upb::EnumDefPtr descriptor) {
102 return ToCIdent(descriptor.full_name()) + "_enum_init";
103 }
104
EnumMiniTableRef(upb::EnumDefPtr descriptor,const Options & options)105 std::string EnumMiniTableRef(upb::EnumDefPtr descriptor,
106 const Options& options) {
107 if (options.bootstrap) {
108 return absl::StrCat(EnumInitName(descriptor), "()");
109 } else {
110 return absl::StrCat("&", EnumInitName(descriptor));
111 }
112 }
113
ExtensionIdentBase(upb::FieldDefPtr ext)114 std::string ExtensionIdentBase(upb::FieldDefPtr ext) {
115 assert(ext.is_extension());
116 std::string ext_scope;
117 if (ext.extension_scope()) {
118 return MessageName(ext.extension_scope());
119 } else {
120 return ToCIdent(ext.file().package());
121 }
122 }
123
ExtensionLayout(upb::FieldDefPtr ext)124 std::string ExtensionLayout(upb::FieldDefPtr ext) {
125 return absl::StrCat(ExtensionIdentBase(ext), "_", ext.name(), "_ext");
126 }
127
128 const char* kEnumsInit = "enums_layout";
129 const char* kExtensionsInit = "extensions_layout";
130 const char* kMessagesInit = "messages_layout";
131
EnumValueSymbol(upb::EnumValDefPtr value)132 std::string EnumValueSymbol(upb::EnumValDefPtr value) {
133 return ToCIdent(value.full_name());
134 }
135
CTypeInternal(upb::FieldDefPtr field,bool is_const)136 std::string CTypeInternal(upb::FieldDefPtr field, bool is_const) {
137 std::string maybe_const = is_const ? "const " : "";
138 switch (field.ctype()) {
139 case kUpb_CType_Message: {
140 std::string maybe_struct =
141 field.file() != field.message_type().file() ? "struct " : "";
142 return maybe_const + maybe_struct + MessageName(field.message_type()) +
143 "*";
144 }
145 case kUpb_CType_Bool:
146 return "bool";
147 case kUpb_CType_Float:
148 return "float";
149 case kUpb_CType_Int32:
150 case kUpb_CType_Enum:
151 return "int32_t";
152 case kUpb_CType_UInt32:
153 return "uint32_t";
154 case kUpb_CType_Double:
155 return "double";
156 case kUpb_CType_Int64:
157 return "int64_t";
158 case kUpb_CType_UInt64:
159 return "uint64_t";
160 case kUpb_CType_String:
161 case kUpb_CType_Bytes:
162 return "upb_StringView";
163 default:
164 abort();
165 }
166 }
167
FloatToCLiteral(float value)168 std::string FloatToCLiteral(float value) {
169 if (value == std::numeric_limits<float>::infinity()) {
170 return "kUpb_FltInfinity";
171 } else if (value == -std::numeric_limits<float>::infinity()) {
172 return "-kUpb_FltInfinity";
173 } else if (std::isnan(value)) {
174 return "kUpb_NaN";
175 } else {
176 return absl::StrCat(value);
177 }
178 }
179
DoubleToCLiteral(double value)180 std::string DoubleToCLiteral(double value) {
181 if (value == std::numeric_limits<double>::infinity()) {
182 return "kUpb_Infinity";
183 } else if (value == -std::numeric_limits<double>::infinity()) {
184 return "-kUpb_Infinity";
185 } else if (std::isnan(value)) {
186 return "kUpb_NaN";
187 } else {
188 return absl::StrCat(value);
189 }
190 }
191
FieldDefault(upb::FieldDefPtr field)192 std::string FieldDefault(upb::FieldDefPtr field) {
193 switch (field.ctype()) {
194 case kUpb_CType_Message:
195 return "NULL";
196 case kUpb_CType_Bytes:
197 case kUpb_CType_String: {
198 upb_StringView str = field.default_value().str_val;
199 return absl::Substitute(
200 "upb_StringView_FromString(\"$0\")",
201 absl::CEscape(absl::string_view(str.data, str.size)));
202 }
203 case kUpb_CType_Int32:
204 return absl::Substitute("(int32_t)$0", field.default_value().int32_val);
205 case kUpb_CType_Int64:
206 if (field.default_value().int64_val == INT64_MIN) {
207 // Special-case to avoid:
208 // integer literal is too large to be represented in a signed integer
209 // type, interpreting as unsigned
210 // [-Werror,-Wimplicitly-unsigned-literal]
211 // int64_t default_val = (int64_t)-9223372036854775808ll;
212 //
213 // More info here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661
214 return "INT64_MIN";
215 } else {
216 return absl::Substitute("(int64_t)$0ll",
217 field.default_value().int64_val);
218 }
219 case kUpb_CType_UInt32:
220 return absl::Substitute("(uint32_t)$0u",
221 field.default_value().uint32_val);
222 case kUpb_CType_UInt64:
223 return absl::Substitute("(uint64_t)$0ull",
224 field.default_value().uint64_val);
225 case kUpb_CType_Float:
226 return FloatToCLiteral(field.default_value().float_val);
227 case kUpb_CType_Double:
228 return DoubleToCLiteral(field.default_value().double_val);
229 case kUpb_CType_Bool:
230 return field.default_value().bool_val ? "true" : "false";
231 case kUpb_CType_Enum:
232 // Use a number instead of a symbolic name so that we don't require
233 // this enum's header to be included.
234 return absl::StrCat(field.default_value().int32_val);
235 }
236 ABSL_ASSERT(false);
237 return "XXX";
238 }
239
CType(upb::FieldDefPtr field)240 std::string CType(upb::FieldDefPtr field) {
241 return CTypeInternal(field, false);
242 }
243
CTypeConst(upb::FieldDefPtr field)244 std::string CTypeConst(upb::FieldDefPtr field) {
245 return CTypeInternal(field, true);
246 }
247
MapKeyCType(upb::FieldDefPtr map_field)248 std::string MapKeyCType(upb::FieldDefPtr map_field) {
249 return CType(map_field.message_type().map_key());
250 }
251
MapValueCType(upb::FieldDefPtr map_field)252 std::string MapValueCType(upb::FieldDefPtr map_field) {
253 return CType(map_field.message_type().map_value());
254 }
255
MapKeySize(upb::FieldDefPtr map_field,absl::string_view expr)256 std::string MapKeySize(upb::FieldDefPtr map_field, absl::string_view expr) {
257 return map_field.message_type().map_key().ctype() == kUpb_CType_String
258 ? "0"
259 : absl::StrCat("sizeof(", expr, ")");
260 }
261
MapValueSize(upb::FieldDefPtr map_field,absl::string_view expr)262 std::string MapValueSize(upb::FieldDefPtr map_field, absl::string_view expr) {
263 return map_field.message_type().map_value().ctype() == kUpb_CType_String
264 ? "0"
265 : absl::StrCat("sizeof(", expr, ")");
266 }
267
268 std::string FieldInitializer(const DefPoolPair& pools, upb::FieldDefPtr field,
269 const Options& options);
270
DumpEnumValues(upb::EnumDefPtr desc,Output & output)271 void DumpEnumValues(upb::EnumDefPtr desc, Output& output) {
272 std::vector<upb::EnumValDefPtr> values;
273 values.reserve(desc.value_count());
274 for (int i = 0; i < desc.value_count(); i++) {
275 values.push_back(desc.value(i));
276 }
277 std::sort(values.begin(), values.end(),
278 [](upb::EnumValDefPtr a, upb::EnumValDefPtr b) {
279 return a.number() < b.number();
280 });
281
282 for (size_t i = 0; i < values.size(); i++) {
283 auto value = values[i];
284 output(" $0 = $1", EnumValueSymbol(value), value.number());
285 if (i != values.size() - 1) {
286 output(",");
287 }
288 output("\n");
289 }
290 }
291
292 std::string GetFieldRep(const DefPoolPair& pools, upb::FieldDefPtr field);
293
GenerateExtensionInHeader(const DefPoolPair & pools,upb::FieldDefPtr ext,Output & output)294 void GenerateExtensionInHeader(const DefPoolPair& pools, upb::FieldDefPtr ext,
295 Output& output) {
296 output(
297 R"cc(
298 UPB_INLINE bool $0_has_$1(const struct $2* msg) {
299 return _upb_Message_HasExtensionField(msg, &$3);
300 }
301 )cc",
302 ExtensionIdentBase(ext), ext.name(), MessageName(ext.containing_type()),
303 ExtensionLayout(ext));
304
305 output(
306 R"cc(
307 UPB_INLINE void $0_clear_$1(struct $2* msg) {
308 _upb_Message_ClearExtensionField(msg, &$3);
309 }
310 )cc",
311 ExtensionIdentBase(ext), ext.name(), MessageName(ext.containing_type()),
312 ExtensionLayout(ext));
313
314 if (ext.IsSequence()) {
315 // TODO(b/259861668): We need generated accessors for repeated extensions.
316 } else {
317 output(
318 R"cc(
319 UPB_INLINE $0 $1_$2(const struct $3* msg) {
320 const upb_MiniTableExtension* ext = &$4;
321 UPB_ASSUME(!upb_IsRepeatedOrMap(&ext->field));
322 UPB_ASSUME(_upb_MiniTableField_GetRep(&ext->field) == $5);
323 $0 default_val = $6;
324 $0 ret;
325 _upb_Message_GetExtensionField(msg, ext, &default_val, &ret);
326 return ret;
327 }
328 )cc",
329 CTypeConst(ext), ExtensionIdentBase(ext), ext.name(),
330 MessageName(ext.containing_type()), ExtensionLayout(ext),
331 GetFieldRep(pools, ext), FieldDefault(ext));
332 output(
333 R"cc(
334 UPB_INLINE void $1_set_$2(struct $3* msg, $0 val, upb_Arena* arena) {
335 const upb_MiniTableExtension* ext = &$4;
336 UPB_ASSUME(!upb_IsRepeatedOrMap(&ext->field));
337 UPB_ASSUME(_upb_MiniTableField_GetRep(&ext->field) == $5);
338 bool ok = _upb_Message_SetExtensionField(msg, ext, &val, arena);
339 UPB_ASSERT(ok);
340 }
341 )cc",
342 CTypeConst(ext), ExtensionIdentBase(ext), ext.name(),
343 MessageName(ext.containing_type()), ExtensionLayout(ext),
344 GetFieldRep(pools, ext));
345 }
346 }
347
GenerateMessageFunctionsInHeader(upb::MessageDefPtr message,const Options & options,Output & output)348 void GenerateMessageFunctionsInHeader(upb::MessageDefPtr message,
349 const Options& options, Output& output) {
350 // TODO(b/235839510): The generated code here does not check the return values
351 // from upb_Encode(). How can we even fix this without breaking other things?
352 output(
353 R"cc(
354 UPB_INLINE $0* $0_new(upb_Arena* arena) {
355 return ($0*)_upb_Message_New($1, arena);
356 }
357 UPB_INLINE $0* $0_parse(const char* buf, size_t size, upb_Arena* arena) {
358 $0* ret = $0_new(arena);
359 if (!ret) return NULL;
360 if (upb_Decode(buf, size, ret, $1, NULL, 0, arena) != kUpb_DecodeStatus_Ok) {
361 return NULL;
362 }
363 return ret;
364 }
365 UPB_INLINE $0* $0_parse_ex(const char* buf, size_t size,
366 const upb_ExtensionRegistry* extreg,
367 int options, upb_Arena* arena) {
368 $0* ret = $0_new(arena);
369 if (!ret) return NULL;
370 if (upb_Decode(buf, size, ret, $1, extreg, options, arena) !=
371 kUpb_DecodeStatus_Ok) {
372 return NULL;
373 }
374 return ret;
375 }
376 UPB_INLINE char* $0_serialize(const $0* msg, upb_Arena* arena, size_t* len) {
377 char* ptr;
378 (void)upb_Encode(msg, $1, 0, arena, &ptr, len);
379 return ptr;
380 }
381 UPB_INLINE char* $0_serialize_ex(const $0* msg, int options,
382 upb_Arena* arena, size_t* len) {
383 char* ptr;
384 (void)upb_Encode(msg, $1, options, arena, &ptr, len);
385 return ptr;
386 }
387 )cc",
388 MessageName(message), MessageMiniTableRef(message, options));
389 }
390
GenerateOneofInHeader(upb::OneofDefPtr oneof,const DefPoolPair & pools,absl::string_view msg_name,const Options & options,Output & output)391 void GenerateOneofInHeader(upb::OneofDefPtr oneof, const DefPoolPair& pools,
392 absl::string_view msg_name, const Options& options,
393 Output& output) {
394 std::string fullname = ToCIdent(oneof.full_name());
395 output("typedef enum {\n");
396 for (int j = 0; j < oneof.field_count(); j++) {
397 upb::FieldDefPtr field = oneof.field(j);
398 output(" $0_$1 = $2,\n", fullname, field.name(), field.number());
399 }
400 output(
401 " $0_NOT_SET = 0\n"
402 "} $0_oneofcases;\n",
403 fullname);
404 output(
405 R"cc(
406 UPB_INLINE $0_oneofcases $1_$2_case(const $1* msg) {
407 const upb_MiniTableField field = $3;
408 return ($0_oneofcases)upb_Message_WhichOneofFieldNumber(msg, &field);
409 }
410 )cc",
411 fullname, msg_name, oneof.name(),
412 FieldInitializer(pools, oneof.field(0), options));
413 }
414
GenerateHazzer(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)415 void GenerateHazzer(upb::FieldDefPtr field, const DefPoolPair& pools,
416 absl::string_view msg_name,
417 const NameToFieldDefMap& field_names,
418 const Options& options, Output& output) {
419 std::string resolved_name = ResolveFieldName(field, field_names);
420 if (field.has_presence()) {
421 output(
422 R"cc(
423 UPB_INLINE bool $0_has_$1(const $0* msg) {
424 const upb_MiniTableField field = $2;
425 return _upb_Message_HasNonExtensionField(msg, &field);
426 }
427 )cc",
428 msg_name, resolved_name, FieldInitializer(pools, field, options));
429 } else if (field.IsMap()) {
430 // Do nothing.
431 } else if (field.IsSequence()) {
432 // TODO(b/259616267): remove.
433 output(
434 R"cc(
435 UPB_INLINE bool $0_has_$1(const $0* msg) {
436 size_t size;
437 $0_$1(msg, &size);
438 return size != 0;
439 }
440 )cc",
441 msg_name, resolved_name);
442 }
443 }
444
GenerateClear(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)445 void GenerateClear(upb::FieldDefPtr field, const DefPoolPair& pools,
446 absl::string_view msg_name,
447 const NameToFieldDefMap& field_names, const Options& options,
448 Output& output) {
449 if (field == field.containing_type().map_key() ||
450 field == field.containing_type().map_value()) {
451 // Cannot be cleared.
452 return;
453 }
454 std::string resolved_name = ResolveFieldName(field, field_names);
455 output(
456 R"cc(
457 UPB_INLINE void $0_clear_$1($0* msg) {
458 const upb_MiniTableField field = $2;
459 _upb_Message_ClearNonExtensionField(msg, &field);
460 }
461 )cc",
462 msg_name, resolved_name, FieldInitializer(pools, field, options));
463 }
464
GenerateMapGetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)465 void GenerateMapGetters(upb::FieldDefPtr field, const DefPoolPair& pools,
466 absl::string_view msg_name,
467 const NameToFieldDefMap& field_names,
468 const Options& options, Output& output) {
469 std::string resolved_name = ResolveFieldName(field, field_names);
470 output(
471 R"cc(
472 UPB_INLINE size_t $0_$1_size(const $0* msg) {
473 const upb_MiniTableField field = $2;
474 const upb_Map* map = upb_Message_GetMap(msg, &field);
475 return map ? _upb_Map_Size(map) : 0;
476 }
477 )cc",
478 msg_name, resolved_name, FieldInitializer(pools, field, options));
479 output(
480 R"cc(
481 UPB_INLINE bool $0_$1_get(const $0* msg, $2 key, $3* val) {
482 const upb_MiniTableField field = $4;
483 const upb_Map* map = upb_Message_GetMap(msg, &field);
484 if (!map) return false;
485 return _upb_Map_Get(map, &key, $5, val, $6);
486 }
487 )cc",
488 msg_name, resolved_name, MapKeyCType(field), MapValueCType(field),
489 FieldInitializer(pools, field, options), MapKeySize(field, "key"),
490 MapValueSize(field, "*val"));
491 output(
492 R"cc(
493 UPB_INLINE $0 $1_$2_next(const $1* msg, size_t* iter) {
494 const upb_MiniTableField field = $3;
495 const upb_Map* map = upb_Message_GetMap(msg, &field);
496 if (!map) return NULL;
497 return ($0)_upb_map_next(map, iter);
498 }
499 )cc",
500 CTypeConst(field), msg_name, resolved_name,
501 FieldInitializer(pools, field, options));
502 }
503
GenerateMapEntryGetters(upb::FieldDefPtr field,absl::string_view msg_name,Output & output)504 void GenerateMapEntryGetters(upb::FieldDefPtr field, absl::string_view msg_name,
505 Output& output) {
506 output(
507 R"cc(
508 UPB_INLINE $0 $1_$2(const $1* msg) {
509 $3 ret;
510 _upb_msg_map_$2(msg, &ret, $4);
511 return ret;
512 }
513 )cc",
514 CTypeConst(field), msg_name, field.name(), CType(field),
515 field.ctype() == kUpb_CType_String ? "0" : "sizeof(ret)");
516 }
517
GenerateRepeatedGetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)518 void GenerateRepeatedGetters(upb::FieldDefPtr field, const DefPoolPair& pools,
519 absl::string_view msg_name,
520 const NameToFieldDefMap& field_names,
521 const Options& options, Output& output) {
522 // Generate getter returning first item and size.
523 //
524 // Example:
525 // UPB_INLINE const struct Bar* const* name(const Foo* msg, size_t* size)
526 output(
527 R"cc(
528 UPB_INLINE $0 const* $1_$2(const $1* msg, size_t* size) {
529 const upb_MiniTableField field = $3;
530 const upb_Array* arr = upb_Message_GetArray(msg, &field);
531 if (arr) {
532 if (size) *size = arr->size;
533 return ($0 const*)_upb_array_constptr(arr);
534 } else {
535 if (size) *size = 0;
536 return NULL;
537 }
538 }
539 )cc",
540 CTypeConst(field), // $0
541 msg_name, // $1
542 ResolveFieldName(field, field_names), // $2
543 FieldInitializer(pools, field, options) // #3
544 );
545 // Generate private getter returning array or NULL for immutable and upb_Array
546 // for mutable.
547 //
548 // Example:
549 // UPB_INLINE const upb_Array* _name_upbarray(size_t* size)
550 // UPB_INLINE upb_Array* _name_mutable_upbarray(size_t* size)
551 output(
552 R"cc(
553 UPB_INLINE const upb_Array* _$1_$2_$4(const $1* msg, size_t* size) {
554 const upb_MiniTableField field = $3;
555 const upb_Array* arr = upb_Message_GetArray(msg, &field);
556 if (size) {
557 *size = arr ? arr->size : 0;
558 }
559 return arr;
560 }
561 UPB_INLINE upb_Array* _$1_$2_$5(const $1* msg, size_t* size, upb_Arena* arena) {
562 const upb_MiniTableField field = $3;
563 upb_Array* arr = upb_Message_GetOrCreateMutableArray(
564 (upb_Message*)msg, &field, arena);
565 if (size) {
566 *size = arr ? arr->size : 0;
567 }
568 return arr;
569 }
570 )cc",
571 CTypeConst(field), // $0
572 msg_name, // $1
573 ResolveFieldName(field, field_names), // $2
574 FieldInitializer(pools, field, options), // $3
575 kRepeatedFieldArrayGetterPostfix, // $4
576 kRepeatedFieldMutableArrayGetterPostfix // $5
577 );
578 }
579
GenerateScalarGetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & Options,Output & output)580 void GenerateScalarGetters(upb::FieldDefPtr field, const DefPoolPair& pools,
581 absl::string_view msg_name,
582 const NameToFieldDefMap& field_names,
583 const Options& Options, Output& output) {
584 std::string field_name = ResolveFieldName(field, field_names);
585 output(
586 R"cc(
587 UPB_INLINE $0 $1_$2(const $1* msg) {
588 $0 default_val = $3;
589 $0 ret;
590 const upb_MiniTableField field = $4;
591 _upb_Message_GetNonExtensionField(msg, &field, &default_val, &ret);
592 return ret;
593 }
594 )cc",
595 CTypeConst(field), msg_name, field_name, FieldDefault(field),
596 FieldInitializer(pools, field, Options));
597 }
598
GenerateGetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)599 void GenerateGetters(upb::FieldDefPtr field, const DefPoolPair& pools,
600 absl::string_view msg_name,
601 const NameToFieldDefMap& field_names,
602 const Options& options, Output& output) {
603 if (field.IsMap()) {
604 GenerateMapGetters(field, pools, msg_name, field_names, options, output);
605 } else if (UPB_DESC(MessageOptions_map_entry)(
606 field.containing_type().options())) {
607 GenerateMapEntryGetters(field, msg_name, output);
608 } else if (field.IsSequence()) {
609 GenerateRepeatedGetters(field, pools, msg_name, field_names, options,
610 output);
611 } else {
612 GenerateScalarGetters(field, pools, msg_name, field_names, options, output);
613 }
614 }
615
GenerateMapSetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)616 void GenerateMapSetters(upb::FieldDefPtr field, const DefPoolPair& pools,
617 absl::string_view msg_name,
618 const NameToFieldDefMap& field_names,
619 const Options& options, Output& output) {
620 std::string resolved_name = ResolveFieldName(field, field_names);
621 output(
622 R"cc(
623 UPB_INLINE void $0_$1_clear($0* msg) {
624 const upb_MiniTableField field = $2;
625 upb_Map* map = (upb_Map*)upb_Message_GetMap(msg, &field);
626 if (!map) return;
627 _upb_Map_Clear(map);
628 }
629 )cc",
630 msg_name, resolved_name, FieldInitializer(pools, field, options));
631 output(
632 R"cc(
633 UPB_INLINE bool $0_$1_set($0* msg, $2 key, $3 val, upb_Arena* a) {
634 const upb_MiniTableField field = $4;
635 upb_Map* map = _upb_Message_GetOrCreateMutableMap(msg, &field, $5, $6, a);
636 return _upb_Map_Insert(map, &key, $5, &val, $6, a) !=
637 kUpb_MapInsertStatus_OutOfMemory;
638 }
639 )cc",
640 msg_name, resolved_name, MapKeyCType(field), MapValueCType(field),
641 FieldInitializer(pools, field, options), MapKeySize(field, "key"),
642 MapValueSize(field, "val"));
643 output(
644 R"cc(
645 UPB_INLINE bool $0_$1_delete($0* msg, $2 key) {
646 const upb_MiniTableField field = $3;
647 upb_Map* map = (upb_Map*)upb_Message_GetMap(msg, &field);
648 if (!map) return false;
649 return _upb_Map_Delete(map, &key, $4, NULL);
650 }
651 )cc",
652 msg_name, resolved_name, MapKeyCType(field),
653 FieldInitializer(pools, field, options), MapKeySize(field, "key"));
654 output(
655 R"cc(
656 UPB_INLINE $0 $1_$2_nextmutable($1* msg, size_t* iter) {
657 const upb_MiniTableField field = $3;
658 upb_Map* map = (upb_Map*)upb_Message_GetMap(msg, &field);
659 if (!map) return NULL;
660 return ($0)_upb_map_next(map, iter);
661 }
662 )cc",
663 CType(field), msg_name, resolved_name,
664 FieldInitializer(pools, field, options));
665 }
666
GenerateRepeatedSetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)667 void GenerateRepeatedSetters(upb::FieldDefPtr field, const DefPoolPair& pools,
668 absl::string_view msg_name,
669 const NameToFieldDefMap& field_names,
670 const Options& options, Output& output) {
671 std::string resolved_name = ResolveFieldName(field, field_names);
672 output(
673 R"cc(
674 UPB_INLINE $0* $1_mutable_$2($1* msg, size_t* size) {
675 upb_MiniTableField field = $3;
676 upb_Array* arr = upb_Message_GetMutableArray(msg, &field);
677 if (arr) {
678 if (size) *size = arr->size;
679 return ($0*)_upb_array_ptr(arr);
680 } else {
681 if (size) *size = 0;
682 return NULL;
683 }
684 }
685 )cc",
686 CType(field), msg_name, resolved_name,
687 FieldInitializer(pools, field, options));
688 output(
689 R"cc(
690 UPB_INLINE $0* $1_resize_$2($1* msg, size_t size, upb_Arena* arena) {
691 upb_MiniTableField field = $3;
692 return ($0*)upb_Message_ResizeArray(msg, &field, size, arena);
693 }
694 )cc",
695 CType(field), msg_name, resolved_name,
696 FieldInitializer(pools, field, options));
697 if (field.ctype() == kUpb_CType_Message) {
698 output(
699 R"cc(
700 UPB_INLINE struct $0* $1_add_$2($1* msg, upb_Arena* arena) {
701 upb_MiniTableField field = $4;
702 upb_Array* arr = upb_Message_GetOrCreateMutableArray(msg, &field, arena);
703 if (!arr || !_upb_Array_ResizeUninitialized(arr, arr->size + 1, arena)) {
704 return NULL;
705 }
706 struct $0* sub = (struct $0*)_upb_Message_New($3, arena);
707 if (!arr || !sub) return NULL;
708 _upb_Array_Set(arr, arr->size - 1, &sub, sizeof(sub));
709 return sub;
710 }
711 )cc",
712 MessageName(field.message_type()), msg_name, resolved_name,
713 MessageMiniTableRef(field.message_type(), options),
714 FieldInitializer(pools, field, options));
715 } else {
716 output(
717 R"cc(
718 UPB_INLINE bool $1_add_$2($1* msg, $0 val, upb_Arena* arena) {
719 upb_MiniTableField field = $3;
720 upb_Array* arr = upb_Message_GetOrCreateMutableArray(msg, &field, arena);
721 if (!arr || !_upb_Array_ResizeUninitialized(arr, arr->size + 1, arena)) {
722 return false;
723 }
724 _upb_Array_Set(arr, arr->size - 1, &val, sizeof(val));
725 return true;
726 }
727 )cc",
728 CType(field), msg_name, resolved_name,
729 FieldInitializer(pools, field, options));
730 }
731 }
732
GenerateNonRepeatedSetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)733 void GenerateNonRepeatedSetters(upb::FieldDefPtr field,
734 const DefPoolPair& pools,
735 absl::string_view msg_name,
736 const NameToFieldDefMap& field_names,
737 const Options& options, Output& output) {
738 if (field == field.containing_type().map_key()) {
739 // Key cannot be mutated.
740 return;
741 }
742
743 std::string field_name = ResolveFieldName(field, field_names);
744
745 if (field == field.containing_type().map_value()) {
746 output(R"cc(
747 UPB_INLINE void $0_set_$1($0 *msg, $2 value) {
748 _upb_msg_map_set_value(msg, &value, $3);
749 }
750 )cc",
751 msg_name, field_name, CType(field),
752 field.ctype() == kUpb_CType_String ? "0"
753 : "sizeof(" + CType(field) + ")");
754 } else {
755 output(R"cc(
756 UPB_INLINE void $0_set_$1($0 *msg, $2 value) {
757 const upb_MiniTableField field = $3;
758 _upb_Message_SetNonExtensionField(msg, &field, &value);
759 }
760 )cc",
761 msg_name, field_name, CType(field),
762 FieldInitializer(pools, field, options));
763 }
764
765 // Message fields also have a Msg_mutable_foo() accessor that will create
766 // the sub-message if it doesn't already exist.
767 if (field.ctype() == kUpb_CType_Message &&
768 !UPB_DESC(MessageOptions_map_entry)(field.containing_type().options())) {
769 output(
770 R"cc(
771 UPB_INLINE struct $0* $1_mutable_$2($1* msg, upb_Arena* arena) {
772 struct $0* sub = (struct $0*)$1_$2(msg);
773 if (sub == NULL) {
774 sub = (struct $0*)_upb_Message_New($3, arena);
775 if (sub) $1_set_$2(msg, sub);
776 }
777 return sub;
778 }
779 )cc",
780 MessageName(field.message_type()), msg_name, field_name,
781 MessageMiniTableRef(field.message_type(), options));
782 }
783 }
784
GenerateSetters(upb::FieldDefPtr field,const DefPoolPair & pools,absl::string_view msg_name,const NameToFieldDefMap & field_names,const Options & options,Output & output)785 void GenerateSetters(upb::FieldDefPtr field, const DefPoolPair& pools,
786 absl::string_view msg_name,
787 const NameToFieldDefMap& field_names,
788 const Options& options, Output& output) {
789 if (field.IsMap()) {
790 GenerateMapSetters(field, pools, msg_name, field_names, options, output);
791 } else if (field.IsSequence()) {
792 GenerateRepeatedSetters(field, pools, msg_name, field_names, options,
793 output);
794 } else {
795 GenerateNonRepeatedSetters(field, pools, msg_name, field_names, options,
796 output);
797 }
798 }
799
GenerateMessageInHeader(upb::MessageDefPtr message,const DefPoolPair & pools,const Options & options,Output & output)800 void GenerateMessageInHeader(upb::MessageDefPtr message,
801 const DefPoolPair& pools, const Options& options,
802 Output& output) {
803 output("/* $0 */\n\n", message.full_name());
804 std::string msg_name = ToCIdent(message.full_name());
805 if (!UPB_DESC(MessageOptions_map_entry)(message.options())) {
806 GenerateMessageFunctionsInHeader(message, options, output);
807 }
808
809 for (int i = 0; i < message.real_oneof_count(); i++) {
810 GenerateOneofInHeader(message.oneof(i), pools, msg_name, options, output);
811 }
812
813 auto field_names = CreateFieldNameMap(message);
814 for (auto field : FieldNumberOrder(message)) {
815 GenerateClear(field, pools, msg_name, field_names, options, output);
816 GenerateGetters(field, pools, msg_name, field_names, options, output);
817 GenerateHazzer(field, pools, msg_name, field_names, options, output);
818 }
819
820 output("\n");
821
822 for (auto field : FieldNumberOrder(message)) {
823 GenerateSetters(field, pools, msg_name, field_names, options, output);
824 }
825
826 output("\n");
827 }
828
ForwardDeclareMiniTableInit(upb::MessageDefPtr message,const Options & options,Output & output)829 void ForwardDeclareMiniTableInit(upb::MessageDefPtr message,
830 const Options& options, Output& output) {
831 if (options.bootstrap) {
832 output("extern const upb_MiniTable* $0();\n", MessageInitName(message));
833 } else {
834 output("extern const upb_MiniTable $0;\n", MessageInitName(message));
835 }
836 }
837
WriteHeader(const DefPoolPair & pools,upb::FileDefPtr file,const Options & options,Output & output)838 void WriteHeader(const DefPoolPair& pools, upb::FileDefPtr file,
839 const Options& options, Output& output) {
840 EmitFileWarning(file.name(), output);
841 output(
842 "#ifndef $0_UPB_H_\n"
843 "#define $0_UPB_H_\n\n"
844 "#include \"upb/collections/array_internal.h\"\n"
845 "#include \"upb/collections/map_gencode_util.h\"\n"
846 "#include \"upb/message/accessors.h\"\n"
847 "#include \"upb/message/internal.h\"\n"
848 "#include \"upb/mini_table/enum_internal.h\"\n"
849 "#include \"upb/wire/decode.h\"\n"
850 "#include \"upb/wire/decode_fast.h\"\n"
851 "#include \"upb/wire/encode.h\"\n\n",
852 ToPreproc(file.name()));
853
854 for (int i = 0; i < file.public_dependency_count(); i++) {
855 if (i == 0) {
856 output("/* Public Imports. */\n");
857 }
858 output("#include \"$0\"\n", HeaderFilename(file.public_dependency(i)));
859 if (i == file.public_dependency_count() - 1) {
860 output("\n");
861 }
862 }
863
864 output(
865 "// Must be last. \n"
866 "#include \"upb/port/def.inc\"\n"
867 "\n"
868 "#ifdef __cplusplus\n"
869 "extern \"C\" {\n"
870 "#endif\n"
871 "\n");
872
873 const std::vector<upb::MessageDefPtr> this_file_messages =
874 SortedMessages(file);
875 const std::vector<upb::FieldDefPtr> this_file_exts = SortedExtensions(file);
876
877 // Forward-declare types defined in this file.
878 for (auto message : this_file_messages) {
879 output("typedef struct $0 $0;\n", ToCIdent(message.full_name()));
880 }
881 for (auto message : this_file_messages) {
882 ForwardDeclareMiniTableInit(message, options, output);
883 }
884 for (auto ext : this_file_exts) {
885 output("extern const upb_MiniTableExtension $0;\n", ExtensionLayout(ext));
886 }
887
888 // Forward-declare types not in this file, but used as submessages.
889 // Order by full name for consistent ordering.
890 std::map<std::string, upb::MessageDefPtr> forward_messages;
891
892 for (auto message : this_file_messages) {
893 for (int i = 0; i < message.field_count(); i++) {
894 upb::FieldDefPtr field = message.field(i);
895 if (field.ctype() == kUpb_CType_Message &&
896 field.file() != field.message_type().file()) {
897 forward_messages[field.message_type().full_name()] =
898 field.message_type();
899 }
900 }
901 }
902 for (auto ext : this_file_exts) {
903 if (ext.file() != ext.containing_type().file()) {
904 forward_messages[ext.containing_type().full_name()] =
905 ext.containing_type();
906 }
907 }
908 for (const auto& pair : forward_messages) {
909 output("struct $0;\n", MessageName(pair.second));
910 }
911 for (const auto& pair : forward_messages) {
912 ForwardDeclareMiniTableInit(pair.second, options, output);
913 }
914
915 if (!this_file_messages.empty()) {
916 output("\n");
917 }
918
919 std::vector<upb::EnumDefPtr> this_file_enums = SortedEnums(file);
920
921 for (auto enumdesc : this_file_enums) {
922 output("typedef enum {\n");
923 DumpEnumValues(enumdesc, output);
924 output("} $0;\n\n", ToCIdent(enumdesc.full_name()));
925 }
926
927 output("\n");
928
929 if (file.syntax() == kUpb_Syntax_Proto2) {
930 for (const auto enumdesc : this_file_enums) {
931 if (options.bootstrap) {
932 output("extern const upb_MiniTableEnum* $0();\n", EnumInit(enumdesc));
933 } else {
934 output("extern const upb_MiniTableEnum $0;\n", EnumInit(enumdesc));
935 }
936 }
937 }
938
939 output("\n");
940 for (auto message : this_file_messages) {
941 GenerateMessageInHeader(message, pools, options, output);
942 }
943
944 for (auto ext : this_file_exts) {
945 GenerateExtensionInHeader(pools, ext, output);
946 }
947
948 output("extern const upb_MiniTableFile $0;\n\n", FileLayoutName(file));
949
950 if (absl::string_view(file.name()) == "google/protobuf/descriptor.proto" ||
951 absl::string_view(file.name()) == "net/proto2/proto/descriptor.proto") {
952 // This is gratuitously inefficient with how many times it rebuilds
953 // MessageLayout objects for the same message. But we only do this for one
954 // proto (descriptor.proto) so we don't worry about it.
955 upb::MessageDefPtr max32_message;
956 upb::MessageDefPtr max64_message;
957 size_t max32 = 0;
958 size_t max64 = 0;
959 for (const auto message : this_file_messages) {
960 if (absl::EndsWith(message.name(), "Options")) {
961 size_t size32 = pools.GetMiniTable32(message)->size;
962 size_t size64 = pools.GetMiniTable64(message)->size;
963 if (size32 > max32) {
964 max32 = size32;
965 max32_message = message;
966 }
967 if (size64 > max64) {
968 max64 = size64;
969 max64_message = message;
970 }
971 }
972 }
973
974 output("/* Max size 32 is $0 */\n", max32_message.full_name());
975 output("/* Max size 64 is $0 */\n", max64_message.full_name());
976 output("#define _UPB_MAXOPT_SIZE UPB_SIZE($0, $1)\n\n", max32, max64);
977 }
978
979 output(
980 "#ifdef __cplusplus\n"
981 "} /* extern \"C\" */\n"
982 "#endif\n"
983 "\n"
984 "#include \"upb/port/undef.inc\"\n"
985 "\n"
986 "#endif /* $0_UPB_H_ */\n",
987 ToPreproc(file.name()));
988 }
989
990 typedef std::pair<std::string, uint64_t> TableEntry;
991
GetWireTypeForField(upb::FieldDefPtr field)992 uint32_t GetWireTypeForField(upb::FieldDefPtr field) {
993 if (field.packed()) return kUpb_WireType_Delimited;
994 switch (field.type()) {
995 case kUpb_FieldType_Double:
996 case kUpb_FieldType_Fixed64:
997 case kUpb_FieldType_SFixed64:
998 return kUpb_WireType_64Bit;
999 case kUpb_FieldType_Float:
1000 case kUpb_FieldType_Fixed32:
1001 case kUpb_FieldType_SFixed32:
1002 return kUpb_WireType_32Bit;
1003 case kUpb_FieldType_Int64:
1004 case kUpb_FieldType_UInt64:
1005 case kUpb_FieldType_Int32:
1006 case kUpb_FieldType_Bool:
1007 case kUpb_FieldType_UInt32:
1008 case kUpb_FieldType_Enum:
1009 case kUpb_FieldType_SInt32:
1010 case kUpb_FieldType_SInt64:
1011 return kUpb_WireType_Varint;
1012 case kUpb_FieldType_Group:
1013 return kUpb_WireType_StartGroup;
1014 case kUpb_FieldType_Message:
1015 case kUpb_FieldType_String:
1016 case kUpb_FieldType_Bytes:
1017 return kUpb_WireType_Delimited;
1018 }
1019 UPB_UNREACHABLE();
1020 }
1021
MakeTag(uint32_t field_number,uint32_t wire_type)1022 uint32_t MakeTag(uint32_t field_number, uint32_t wire_type) {
1023 return field_number << 3 | wire_type;
1024 }
1025
WriteVarint32ToArray(uint64_t val,char * buf)1026 size_t WriteVarint32ToArray(uint64_t val, char* buf) {
1027 size_t i = 0;
1028 do {
1029 uint8_t byte = val & 0x7fU;
1030 val >>= 7;
1031 if (val) byte |= 0x80U;
1032 buf[i++] = byte;
1033 } while (val);
1034 return i;
1035 }
1036
GetEncodedTag(upb::FieldDefPtr field)1037 uint64_t GetEncodedTag(upb::FieldDefPtr field) {
1038 uint32_t wire_type = GetWireTypeForField(field);
1039 uint32_t unencoded_tag = MakeTag(field.number(), wire_type);
1040 char tag_bytes[10] = {0};
1041 WriteVarint32ToArray(unencoded_tag, tag_bytes);
1042 uint64_t encoded_tag = 0;
1043 memcpy(&encoded_tag, tag_bytes, sizeof(encoded_tag));
1044 // TODO: byte-swap for big endian.
1045 return encoded_tag;
1046 }
1047
GetTableSlot(upb::FieldDefPtr field)1048 int GetTableSlot(upb::FieldDefPtr field) {
1049 uint64_t tag = GetEncodedTag(field);
1050 if (tag > 0x7fff) {
1051 // Tag must fit within a two-byte varint.
1052 return -1;
1053 }
1054 return (tag & 0xf8) >> 3;
1055 }
1056
TryFillTableEntry(const DefPoolPair & pools,upb::FieldDefPtr field,TableEntry & ent)1057 bool TryFillTableEntry(const DefPoolPair& pools, upb::FieldDefPtr field,
1058 TableEntry& ent) {
1059 const upb_MiniTable* mt = pools.GetMiniTable64(field.containing_type());
1060 const upb_MiniTableField* mt_f =
1061 upb_MiniTable_FindFieldByNumber(mt, field.number());
1062 std::string type = "";
1063 std::string cardinality = "";
1064 switch (upb_MiniTableField_Type(mt_f)) {
1065 case kUpb_FieldType_Bool:
1066 type = "b1";
1067 break;
1068 case kUpb_FieldType_Enum:
1069 if (upb_MiniTableField_IsClosedEnum(mt_f)) {
1070 // We don't have the means to test proto2 enum fields for valid values.
1071 return false;
1072 }
1073 [[fallthrough]];
1074 case kUpb_FieldType_Int32:
1075 case kUpb_FieldType_UInt32:
1076 type = "v4";
1077 break;
1078 case kUpb_FieldType_Int64:
1079 case kUpb_FieldType_UInt64:
1080 type = "v8";
1081 break;
1082 case kUpb_FieldType_Fixed32:
1083 case kUpb_FieldType_SFixed32:
1084 case kUpb_FieldType_Float:
1085 type = "f4";
1086 break;
1087 case kUpb_FieldType_Fixed64:
1088 case kUpb_FieldType_SFixed64:
1089 case kUpb_FieldType_Double:
1090 type = "f8";
1091 break;
1092 case kUpb_FieldType_SInt32:
1093 type = "z4";
1094 break;
1095 case kUpb_FieldType_SInt64:
1096 type = "z8";
1097 break;
1098 case kUpb_FieldType_String:
1099 type = "s";
1100 break;
1101 case kUpb_FieldType_Bytes:
1102 type = "b";
1103 break;
1104 case kUpb_FieldType_Message:
1105 type = "m";
1106 break;
1107 default:
1108 return false; // Not supported yet.
1109 }
1110
1111 switch (upb_FieldMode_Get(mt_f)) {
1112 case kUpb_FieldMode_Map:
1113 return false; // Not supported yet (ever?).
1114 case kUpb_FieldMode_Array:
1115 if (mt_f->mode & kUpb_LabelFlags_IsPacked) {
1116 cardinality = "p";
1117 } else {
1118 cardinality = "r";
1119 }
1120 break;
1121 case kUpb_FieldMode_Scalar:
1122 if (mt_f->presence < 0) {
1123 cardinality = "o";
1124 } else {
1125 cardinality = "s";
1126 }
1127 break;
1128 }
1129
1130 uint64_t expected_tag = GetEncodedTag(field);
1131
1132 // Data is:
1133 //
1134 // 48 32 16 0
1135 // |--------|--------|--------|--------|--------|--------|--------|--------|
1136 // | offset (16) |case offset (16) |presence| submsg | exp. tag (16) |
1137 // |--------|--------|--------|--------|--------|--------|--------|--------|
1138 //
1139 // - |presence| is either hasbit index or field number for oneofs.
1140
1141 uint64_t data = static_cast<uint64_t>(mt_f->offset) << 48 | expected_tag;
1142
1143 if (field.IsSequence()) {
1144 // No hasbit/oneof-related fields.
1145 }
1146 if (field.real_containing_oneof()) {
1147 uint64_t case_offset = ~mt_f->presence;
1148 if (case_offset > 0xffff || field.number() > 0xff) return false;
1149 data |= field.number() << 24;
1150 data |= case_offset << 32;
1151 } else {
1152 uint64_t hasbit_index = 63; // No hasbit (set a high, unused bit).
1153 if (mt_f->presence) {
1154 hasbit_index = mt_f->presence;
1155 if (hasbit_index > 31) return false;
1156 }
1157 data |= hasbit_index << 24;
1158 }
1159
1160 if (field.ctype() == kUpb_CType_Message) {
1161 uint64_t idx = mt_f->UPB_PRIVATE(submsg_index);
1162 if (idx > 255) return false;
1163 data |= idx << 16;
1164
1165 std::string size_ceil = "max";
1166 size_t size = SIZE_MAX;
1167 if (field.message_type().file() == field.file()) {
1168 // We can only be guaranteed the size of the sub-message if it is in the
1169 // same file as us. We could relax this to increase the speed of
1170 // cross-file sub-message parsing if we are comfortable requiring that
1171 // users compile all messages at the same time.
1172 const upb_MiniTable* sub_mt = pools.GetMiniTable64(field.message_type());
1173 size = sub_mt->size + 8;
1174 }
1175 std::vector<size_t> breaks = {64, 128, 192, 256};
1176 for (auto brk : breaks) {
1177 if (size <= brk) {
1178 size_ceil = std::to_string(brk);
1179 break;
1180 }
1181 }
1182 ent.first = absl::Substitute("upb_p$0$1_$2bt_max$3b", cardinality, type,
1183 expected_tag > 0xff ? "2" : "1", size_ceil);
1184
1185 } else {
1186 ent.first = absl::Substitute("upb_p$0$1_$2bt", cardinality, type,
1187 expected_tag > 0xff ? "2" : "1");
1188 }
1189 ent.second = data;
1190 return true;
1191 }
1192
FastDecodeTable(upb::MessageDefPtr message,const DefPoolPair & pools)1193 std::vector<TableEntry> FastDecodeTable(upb::MessageDefPtr message,
1194 const DefPoolPair& pools) {
1195 std::vector<TableEntry> table;
1196 for (const auto field : FieldHotnessOrder(message)) {
1197 TableEntry ent;
1198 int slot = GetTableSlot(field);
1199 // std::cerr << "table slot: " << field->number() << ": " << slot << "\n";
1200 if (slot < 0) {
1201 // Tag can't fit in the table.
1202 continue;
1203 }
1204 if (!TryFillTableEntry(pools, field, ent)) {
1205 // Unsupported field type or offset, hasbit index, etc. doesn't fit.
1206 continue;
1207 }
1208 while ((size_t)slot >= table.size()) {
1209 size_t size = std::max(static_cast<size_t>(1), table.size() * 2);
1210 table.resize(size, TableEntry{"_upb_FastDecoder_DecodeGeneric", 0});
1211 }
1212 if (table[slot].first != "_upb_FastDecoder_DecodeGeneric") {
1213 // A hotter field already filled this slot.
1214 continue;
1215 }
1216 table[slot] = ent;
1217 }
1218 return table;
1219 }
1220
ArchDependentSize(int64_t size32,int64_t size64)1221 std::string ArchDependentSize(int64_t size32, int64_t size64) {
1222 if (size32 == size64) return absl::StrCat(size32);
1223 return absl::Substitute("UPB_SIZE($0, $1)", size32, size64);
1224 }
1225
GetFieldRep(const upb_MiniTableField * field32,const upb_MiniTableField * field64)1226 std::string GetFieldRep(const upb_MiniTableField* field32,
1227 const upb_MiniTableField* field64) {
1228 switch (_upb_MiniTableField_GetRep(field32)) {
1229 case kUpb_FieldRep_1Byte:
1230 return "kUpb_FieldRep_1Byte";
1231 break;
1232 case kUpb_FieldRep_4Byte: {
1233 if (_upb_MiniTableField_GetRep(field64) == kUpb_FieldRep_4Byte) {
1234 return "kUpb_FieldRep_4Byte";
1235 } else {
1236 assert(_upb_MiniTableField_GetRep(field64) == kUpb_FieldRep_8Byte);
1237 return "UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte)";
1238 }
1239 break;
1240 }
1241 case kUpb_FieldRep_StringView:
1242 return "kUpb_FieldRep_StringView";
1243 break;
1244 case kUpb_FieldRep_8Byte:
1245 return "kUpb_FieldRep_8Byte";
1246 break;
1247 }
1248 UPB_UNREACHABLE();
1249 }
1250
GetFieldRep(const DefPoolPair & pools,upb::FieldDefPtr field)1251 std::string GetFieldRep(const DefPoolPair& pools, upb::FieldDefPtr field) {
1252 return GetFieldRep(pools.GetField32(field), pools.GetField64(field));
1253 }
1254
1255 // Returns the field mode as a string initializer.
1256 //
1257 // We could just emit this as a number (and we may yet go in that direction) but
1258 // for now emitting symbolic constants gives this better readability and
1259 // debuggability.
GetModeInit(const upb_MiniTableField * field32,const upb_MiniTableField * field64)1260 std::string GetModeInit(const upb_MiniTableField* field32,
1261 const upb_MiniTableField* field64) {
1262 std::string ret;
1263 uint8_t mode32 = field32->mode;
1264 switch (mode32 & kUpb_FieldMode_Mask) {
1265 case kUpb_FieldMode_Map:
1266 ret = "kUpb_FieldMode_Map";
1267 break;
1268 case kUpb_FieldMode_Array:
1269 ret = "kUpb_FieldMode_Array";
1270 break;
1271 case kUpb_FieldMode_Scalar:
1272 ret = "kUpb_FieldMode_Scalar";
1273 break;
1274 default:
1275 break;
1276 }
1277
1278 if (mode32 & kUpb_LabelFlags_IsPacked) {
1279 absl::StrAppend(&ret, " | kUpb_LabelFlags_IsPacked");
1280 }
1281
1282 if (mode32 & kUpb_LabelFlags_IsExtension) {
1283 absl::StrAppend(&ret, " | kUpb_LabelFlags_IsExtension");
1284 }
1285
1286 if (mode32 & kUpb_LabelFlags_IsAlternate) {
1287 absl::StrAppend(&ret, " | kUpb_LabelFlags_IsAlternate");
1288 }
1289
1290 absl::StrAppend(&ret, " | (", GetFieldRep(field32, field64),
1291 " << kUpb_FieldRep_Shift)");
1292 return ret;
1293 }
1294
FieldInitializer(upb::FieldDefPtr field,const upb_MiniTableField * field64,const upb_MiniTableField * field32,const Options & options)1295 std::string FieldInitializer(upb::FieldDefPtr field,
1296 const upb_MiniTableField* field64,
1297 const upb_MiniTableField* field32,
1298 const Options& options) {
1299 if (options.bootstrap) {
1300 ABSL_CHECK(!field.is_extension());
1301 return absl::Substitute(
1302 "*upb_MiniTable_FindFieldByNumber($0, $1)",
1303 MessageMiniTableRef(field.containing_type(), options), field.number());
1304 } else {
1305 return absl::Substitute(
1306 "{$0, $1, $2, $3, $4, $5}", field64->number,
1307 ArchDependentSize(field32->offset, field64->offset),
1308 ArchDependentSize(field32->presence, field64->presence),
1309 field64->UPB_PRIVATE(submsg_index) == kUpb_NoSub
1310 ? "kUpb_NoSub"
1311 : absl::StrCat(field64->UPB_PRIVATE(submsg_index)).c_str(),
1312 field64->UPB_PRIVATE(descriptortype), GetModeInit(field32, field64));
1313 }
1314 }
1315
FieldInitializer(const DefPoolPair & pools,upb::FieldDefPtr field,const Options & options)1316 std::string FieldInitializer(const DefPoolPair& pools, upb::FieldDefPtr field,
1317 const Options& options) {
1318 return FieldInitializer(field, pools.GetField64(field),
1319 pools.GetField32(field), options);
1320 }
1321
1322 // Writes a single field into a .upb.c source file.
WriteMessageField(upb::FieldDefPtr field,const upb_MiniTableField * field64,const upb_MiniTableField * field32,const Options & options,Output & output)1323 void WriteMessageField(upb::FieldDefPtr field,
1324 const upb_MiniTableField* field64,
1325 const upb_MiniTableField* field32,
1326 const Options& options, Output& output) {
1327 output(" $0,\n", FieldInitializer(field, field64, field32, options));
1328 }
1329
GetSub(upb::FieldDefPtr field)1330 std::string GetSub(upb::FieldDefPtr field) {
1331 if (auto message_def = field.message_type()) {
1332 return absl::Substitute("{.submsg = &$0}", MessageInitName(message_def));
1333 }
1334
1335 if (auto enum_def = field.enum_subdef()) {
1336 if (enum_def.is_closed()) {
1337 return absl::Substitute("{.subenum = &$0}", EnumInit(enum_def));
1338 }
1339 }
1340
1341 return std::string("{.submsg = NULL}");
1342 }
1343
1344 // Writes a single message into a .upb.c source file.
WriteMessage(upb::MessageDefPtr message,const DefPoolPair & pools,const Options & options,Output & output)1345 void WriteMessage(upb::MessageDefPtr message, const DefPoolPair& pools,
1346 const Options& options, Output& output) {
1347 std::string msg_name = ToCIdent(message.full_name());
1348 std::string fields_array_ref = "NULL";
1349 std::string submsgs_array_ref = "NULL";
1350 std::string subenums_array_ref = "NULL";
1351 const upb_MiniTable* mt_32 = pools.GetMiniTable32(message);
1352 const upb_MiniTable* mt_64 = pools.GetMiniTable64(message);
1353 std::vector<std::string> subs;
1354
1355 for (int i = 0; i < mt_64->field_count; i++) {
1356 const upb_MiniTableField* f = &mt_64->fields[i];
1357 if (f->UPB_PRIVATE(submsg_index) != kUpb_NoSub) {
1358 subs.push_back(GetSub(message.FindFieldByNumber(f->number)));
1359 }
1360 }
1361
1362 if (!subs.empty()) {
1363 std::string submsgs_array_name = msg_name + "_submsgs";
1364 submsgs_array_ref = "&" + submsgs_array_name + "[0]";
1365 output("static const upb_MiniTableSub $0[$1] = {\n", submsgs_array_name,
1366 subs.size());
1367
1368 for (const auto& sub : subs) {
1369 output(" $0,\n", sub);
1370 }
1371
1372 output("};\n\n");
1373 }
1374
1375 if (mt_64->field_count > 0) {
1376 std::string fields_array_name = msg_name + "__fields";
1377 fields_array_ref = "&" + fields_array_name + "[0]";
1378 output("static const upb_MiniTableField $0[$1] = {\n", fields_array_name,
1379 mt_64->field_count);
1380 for (int i = 0; i < mt_64->field_count; i++) {
1381 WriteMessageField(message.FindFieldByNumber(mt_64->fields[i].number),
1382 &mt_64->fields[i], &mt_32->fields[i], options, output);
1383 }
1384 output("};\n\n");
1385 }
1386
1387 std::vector<TableEntry> table;
1388 uint8_t table_mask = -1;
1389
1390 table = FastDecodeTable(message, pools);
1391
1392 if (table.size() > 1) {
1393 assert((table.size() & (table.size() - 1)) == 0);
1394 table_mask = (table.size() - 1) << 3;
1395 }
1396
1397 std::string msgext = "kUpb_ExtMode_NonExtendable";
1398
1399 if (message.extension_range_count()) {
1400 if (UPB_DESC(MessageOptions_message_set_wire_format)(message.options())) {
1401 msgext = "kUpb_ExtMode_IsMessageSet";
1402 } else {
1403 msgext = "kUpb_ExtMode_Extendable";
1404 }
1405 }
1406
1407 output("const upb_MiniTable $0 = {\n", MessageInitName(message));
1408 output(" $0,\n", submsgs_array_ref);
1409 output(" $0,\n", fields_array_ref);
1410 output(" $0, $1, $2, $3, UPB_FASTTABLE_MASK($4), $5,\n",
1411 ArchDependentSize(mt_32->size, mt_64->size), mt_64->field_count,
1412 msgext, mt_64->dense_below, table_mask, mt_64->required_count);
1413 if (!table.empty()) {
1414 output(" UPB_FASTTABLE_INIT({\n");
1415 for (const auto& ent : table) {
1416 output(" {0x$1, &$0},\n", ent.first,
1417 absl::StrCat(absl::Hex(ent.second, absl::kZeroPad16)));
1418 }
1419 output(" })\n");
1420 }
1421 output("};\n\n");
1422 }
1423
WriteEnum(upb::EnumDefPtr e,Output & output)1424 void WriteEnum(upb::EnumDefPtr e, Output& output) {
1425 std::string values_init = "{\n";
1426 const upb_MiniTableEnum* mt = e.mini_table();
1427 uint32_t value_count = (mt->mask_limit / 32) + mt->value_count;
1428 for (uint32_t i = 0; i < value_count; i++) {
1429 absl::StrAppend(&values_init, " 0x", absl::Hex(mt->data[i]),
1430 ",\n");
1431 }
1432 values_init += " }";
1433
1434 output(
1435 R"cc(
1436 const upb_MiniTableEnum $0 = {
1437 $1,
1438 $2,
1439 $3,
1440 };
1441 )cc",
1442 EnumInit(e), mt->mask_limit, mt->value_count, values_init);
1443 output("\n");
1444 }
1445
WriteEnums(const DefPoolPair & pools,upb::FileDefPtr file,Output & output)1446 int WriteEnums(const DefPoolPair& pools, upb::FileDefPtr file, Output& output) {
1447 if (file.syntax() != kUpb_Syntax_Proto2) return 0;
1448
1449 std::vector<upb::EnumDefPtr> this_file_enums = SortedEnums(file);
1450
1451 for (const auto e : this_file_enums) {
1452 WriteEnum(e, output);
1453 }
1454
1455 if (!this_file_enums.empty()) {
1456 output("static const upb_MiniTableEnum *$0[$1] = {\n", kEnumsInit,
1457 this_file_enums.size());
1458 for (const auto e : this_file_enums) {
1459 output(" &$0,\n", EnumInit(e));
1460 }
1461 output("};\n");
1462 output("\n");
1463 }
1464
1465 return this_file_enums.size();
1466 }
1467
WriteMessages(const DefPoolPair & pools,upb::FileDefPtr file,const Options & options,Output & output)1468 int WriteMessages(const DefPoolPair& pools, upb::FileDefPtr file,
1469 const Options& options, Output& output) {
1470 std::vector<upb::MessageDefPtr> file_messages = SortedMessages(file);
1471
1472 if (file_messages.empty()) return 0;
1473
1474 for (auto message : file_messages) {
1475 WriteMessage(message, pools, options, output);
1476 }
1477
1478 output("static const upb_MiniTable *$0[$1] = {\n", kMessagesInit,
1479 file_messages.size());
1480 for (auto message : file_messages) {
1481 output(" &$0,\n", MessageInitName(message));
1482 }
1483 output("};\n");
1484 output("\n");
1485 return file_messages.size();
1486 }
1487
WriteExtension(upb::FieldDefPtr ext,const DefPoolPair & pools,const Options & options,Output & output)1488 void WriteExtension(upb::FieldDefPtr ext, const DefPoolPair& pools,
1489 const Options& options, Output& output) {
1490 output("$0,\n", FieldInitializer(pools, ext, options));
1491 output(" &$0,\n", MessageInitName(ext.containing_type()));
1492 output(" $0,\n", GetSub(ext));
1493 }
1494
WriteExtensions(const DefPoolPair & pools,upb::FileDefPtr file,const Options & options,Output & output)1495 int WriteExtensions(const DefPoolPair& pools, upb::FileDefPtr file,
1496 const Options& options, Output& output) {
1497 auto exts = SortedExtensions(file);
1498
1499 if (exts.empty()) return 0;
1500
1501 // Order by full name for consistent ordering.
1502 std::map<std::string, upb::MessageDefPtr> forward_messages;
1503
1504 for (auto ext : exts) {
1505 forward_messages[ext.containing_type().full_name()] = ext.containing_type();
1506 if (ext.message_type()) {
1507 forward_messages[ext.message_type().full_name()] = ext.message_type();
1508 }
1509 }
1510
1511 for (const auto& decl : forward_messages) {
1512 ForwardDeclareMiniTableInit(decl.second, options, output);
1513 }
1514
1515 for (auto ext : exts) {
1516 output("const upb_MiniTableExtension $0 = {\n ", ExtensionLayout(ext));
1517 WriteExtension(ext, pools, options, output);
1518 output("\n};\n");
1519 }
1520
1521 output(
1522 "\n"
1523 "static const upb_MiniTableExtension *$0[$1] = {\n",
1524 kExtensionsInit, exts.size());
1525
1526 for (auto ext : exts) {
1527 output(" &$0,\n", ExtensionLayout(ext));
1528 }
1529
1530 output(
1531 "};\n"
1532 "\n");
1533 return exts.size();
1534 }
1535
WriteMiniTableSource(const DefPoolPair & pools,upb::FileDefPtr file,const Options & options,Output & output)1536 void WriteMiniTableSource(const DefPoolPair& pools, upb::FileDefPtr file,
1537 const Options& options, Output& output) {
1538 EmitFileWarning(file.name(), output);
1539
1540 output(
1541 "#include <stddef.h>\n"
1542 "#include \"upb/collections/array_internal.h\"\n"
1543 "#include \"upb/message/internal.h\"\n"
1544 "#include \"upb/mini_table/enum_internal.h\"\n"
1545 "#include \"$0\"\n",
1546 HeaderFilename(file));
1547
1548 for (int i = 0; i < file.dependency_count(); i++) {
1549 output("#include \"$0\"\n", HeaderFilename(file.dependency(i)));
1550 }
1551
1552 output(
1553 "\n"
1554 "// Must be last.\n"
1555 "#include \"upb/port/def.inc\"\n"
1556 "\n");
1557
1558 int msg_count = WriteMessages(pools, file, options, output);
1559 int ext_count = WriteExtensions(pools, file, options, output);
1560 int enum_count = WriteEnums(pools, file, output);
1561
1562 output("const upb_MiniTableFile $0 = {\n", FileLayoutName(file));
1563 output(" $0,\n", msg_count ? kMessagesInit : "NULL");
1564 output(" $0,\n", enum_count ? kEnumsInit : "NULL");
1565 output(" $0,\n", ext_count ? kExtensionsInit : "NULL");
1566 output(" $0,\n", msg_count);
1567 output(" $0,\n", enum_count);
1568 output(" $0,\n", ext_count);
1569 output("};\n\n");
1570
1571 output("#include \"upb/port/undef.inc\"\n");
1572 output("\n");
1573 }
1574
WriteMessageMiniDescriptorInitializer(upb::MessageDefPtr msg,const Options & options,Output & output)1575 void WriteMessageMiniDescriptorInitializer(upb::MessageDefPtr msg,
1576 const Options& options,
1577 Output& output) {
1578 Output resolve_calls;
1579 for (int i = 0; i < msg.field_count(); i++) {
1580 upb::FieldDefPtr field = msg.field(i);
1581 if (!field.message_type() && !field.enum_subdef()) continue;
1582 if (field.message_type()) {
1583 resolve_calls(
1584 "upb_MiniTable_SetSubMessage(mini_table, "
1585 "(upb_MiniTableField*)upb_MiniTable_FindFieldByNumber(mini_table, "
1586 "$0), $1);\n ",
1587 field.number(), MessageMiniTableRef(field.message_type(), options));
1588 } else if (field.enum_subdef() && field.enum_subdef().is_closed()) {
1589 resolve_calls(
1590 "upb_MiniTable_SetSubEnum(mini_table, "
1591 "(upb_MiniTableField*)upb_MiniTable_FindFieldByNumber(mini_table, "
1592 "$0), $1);\n ",
1593 field.number(), EnumMiniTableRef(field.enum_subdef(), options));
1594 }
1595 }
1596
1597 output(
1598 R"cc(
1599 const upb_MiniTable* $0() {
1600 static upb_MiniTable* mini_table = NULL;
1601 static const char* mini_descriptor = "$1";
1602 if (mini_table) return mini_table;
1603 mini_table =
1604 upb_MiniTable_Build(mini_descriptor, strlen(mini_descriptor),
1605 upb_BootstrapArena(), NULL);
1606 $2return mini_table;
1607 }
1608 )cc",
1609 MessageInitName(msg), msg.MiniDescriptorEncode(), resolve_calls.output());
1610 output("\n");
1611 }
1612
WriteEnumMiniDescriptorInitializer(upb::EnumDefPtr enum_def,const Options & options,Output & output)1613 void WriteEnumMiniDescriptorInitializer(upb::EnumDefPtr enum_def,
1614 const Options& options,
1615 Output& output) {
1616 output(
1617 R"cc(
1618 const upb_MiniTableEnum* $0() {
1619 static const upb_MiniTableEnum* mini_table = NULL;
1620 static const char* mini_descriptor = "$1";
1621 if (mini_table) return mini_table;
1622 mini_table =
1623 upb_MiniTableEnum_Build(mini_descriptor, strlen(mini_descriptor),
1624 upb_BootstrapArena(), NULL);
1625 return mini_table;
1626 }
1627 )cc",
1628 EnumInitName(enum_def), enum_def.MiniDescriptorEncode());
1629 output("\n");
1630 }
1631
WriteMiniDescriptorSource(const DefPoolPair & pools,upb::FileDefPtr file,const Options & options,Output & output)1632 void WriteMiniDescriptorSource(const DefPoolPair& pools, upb::FileDefPtr file,
1633 const Options& options, Output& output) {
1634 output(
1635 "#include <stddef.h>\n"
1636 "#include \"upb/collections/array_internal.h\"\n"
1637 "#include \"upb/message/internal.h\"\n"
1638 "#include \"upb/mini_table/decode.h\"\n"
1639 "#include \"upb/mini_table/enum_internal.h\"\n"
1640 "#include \"$0\"\n\n",
1641 HeaderFilename(file));
1642
1643 for (int i = 0; i < file.dependency_count(); i++) {
1644 output("#include \"$0\"\n", HeaderFilename(file.dependency(i)));
1645 }
1646
1647 output(
1648 R"cc(
1649 static upb_Arena* upb_BootstrapArena() {
1650 static upb_Arena* arena = NULL;
1651 if (!arena) arena = upb_Arena_New();
1652 return arena;
1653 }
1654 )cc");
1655
1656 output("\n");
1657
1658 for (const auto msg : SortedMessages(file)) {
1659 WriteMessageMiniDescriptorInitializer(msg, options, output);
1660 }
1661
1662 for (const auto msg : SortedEnums(file)) {
1663 WriteEnumMiniDescriptorInitializer(msg, options, output);
1664 }
1665 }
1666
WriteSource(const DefPoolPair & pools,upb::FileDefPtr file,const Options & options,Output & output)1667 void WriteSource(const DefPoolPair& pools, upb::FileDefPtr file,
1668 const Options& options, Output& output) {
1669 if (options.bootstrap) {
1670 WriteMiniDescriptorSource(pools, file, options, output);
1671 } else {
1672 WriteMiniTableSource(pools, file, options, output);
1673 }
1674 }
1675
GenerateFile(const DefPoolPair & pools,upb::FileDefPtr file,const Options & options,Plugin * plugin)1676 void GenerateFile(const DefPoolPair& pools, upb::FileDefPtr file,
1677 const Options& options, Plugin* plugin) {
1678 Output h_output;
1679 WriteHeader(pools, file, options, h_output);
1680 plugin->AddOutputFile(HeaderFilename(file), h_output.output());
1681
1682 Output c_output;
1683 WriteSource(pools, file, options, c_output);
1684 plugin->AddOutputFile(SourceFilename(file), c_output.output());
1685 }
1686
ParseOptions(Plugin * plugin,Options * options)1687 bool ParseOptions(Plugin* plugin, Options* options) {
1688 for (const auto& pair : ParseGeneratorParameter(plugin->parameter())) {
1689 if (pair.first == "bootstrap_upb") {
1690 options->bootstrap = true;
1691 } else {
1692 plugin->SetError(absl::Substitute("Unknown parameter: $0", pair.first));
1693 return false;
1694 }
1695 }
1696
1697 return true;
1698 }
1699
ToStringView(upb_StringView str)1700 absl::string_view ToStringView(upb_StringView str) {
1701 return absl::string_view(str.data, str.size);
1702 }
1703
1704 } // namespace
1705
1706 } // namespace upbc
1707
main(int argc,char ** argv)1708 int main(int argc, char** argv) {
1709 upbc::DefPoolPair pools;
1710 upbc::Plugin plugin;
1711 upbc::Options options;
1712 if (!ParseOptions(&plugin, &options)) return 0;
1713 plugin.GenerateFilesRaw([&](const UPB_DESC(FileDescriptorProto) * file_proto,
1714 bool generate) {
1715 upb::Status status;
1716 upb::FileDefPtr file = pools.AddFile(file_proto, &status);
1717 if (!file) {
1718 absl::string_view name =
1719 upbc::ToStringView(UPB_DESC(FileDescriptorProto_name)(file_proto));
1720 ABSL_LOG(FATAL) << "Couldn't add file " << name
1721 << " to DefPool: " << status.error_message();
1722 }
1723 if (generate) GenerateFile(pools, file, options, &plugin);
1724 });
1725 return 0;
1726 }
1727