1 // Protocol Buffers - Google's data interrdchange format
2 // Copyright 2023 Google LLC. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
8 #include "upb_generator/minitable/generator.h"
16 #include "absl/container/flat_hash_set.h"
17 #include "absl/log/absl_check.h"
18 #include "absl/strings/str_cat.h"
19 #include "absl/strings/string_view.h"
20 #include "absl/strings/substitute.h"
21 #include "upb/mini_table/enum.h"
22 #include "upb/mini_table/field.h"
23 #include "upb/mini_table/internal/field.h"
24 #include "upb/mini_table/message.h"
25 #include "upb/reflection/def.hpp"
26 #include "upb_generator/common.h"
27 #include "upb_generator/common/names.h"
28 #include "upb_generator/file_layout.h"
29 #include "upb_generator/minitable/fasttable.h"
30 #include "upb_generator/minitable/names.h"
31 #include "upb_generator/minitable/names_internal.h"
32 #include "upb_generator/plugin.h"
35 #include "upb/port/def.inc"
41 // Some local convenience aliases for MiniTable variable names.
43 std::string
MessageVarName(upb::MessageDefPtr message
) {
44 return MiniTableMessageVarName(message
.full_name());
47 std::string
MessagePtrVarName(upb::MessageDefPtr message
) {
48 return MiniTableMessagePtrVarName(message
.full_name());
51 std::string
EnumVarName(upb::EnumDefPtr e
) {
52 return MiniTableEnumVarName(e
.full_name());
55 std::string
ExtensionVarName(upb::FieldDefPtr ext
) {
56 return MiniTableExtensionVarName(ext
.full_name());
59 std::string
FileVarName(upb::FileDefPtr file
) {
60 return MiniTableFileVarName(file
.name());
63 std::string
HeaderFilename(upb::FileDefPtr file
, bool bootstrap
) {
64 return MiniTableHeaderFilename(file
.name(), bootstrap
);
67 std::string
ArchDependentSize(int64_t size32
, int64_t size64
) {
68 if (size32
== size64
) return absl::StrCat(size32
);
69 return absl::Substitute("UPB_SIZE($0, $1)", size32
, size64
);
72 std::string
FieldInitializer(const DefPoolPair
& pools
, upb::FieldDefPtr field
) {
73 return upb::generator::FieldInitializer(field
, pools
.GetField64(field
),
74 pools
.GetField32(field
));
77 // Writes a single field into a .upb.c source file.
78 void WriteMessageField(upb::FieldDefPtr field
,
79 const upb_MiniTableField
* field64
,
80 const upb_MiniTableField
* field32
, Output
& output
) {
81 output(" $0,\n", upb::generator::FieldInitializer(field
, field64
, field32
));
84 std::string
GetSub(upb::FieldDefPtr field
, bool is_extension
) {
85 if (auto message_def
= field
.message_type()) {
86 return absl::Substitute("{.UPB_PRIVATE(submsg) = &$0}",
87 is_extension
? MessageVarName(message_def
)
88 : MessagePtrVarName(message_def
));
91 if (auto enum_def
= field
.enum_subdef()) {
92 if (enum_def
.is_closed()) {
93 return absl::Substitute("{.UPB_PRIVATE(subenum) = &$0}",
94 MiniTableEnumVarName(enum_def
.full_name()));
98 return std::string("{.UPB_PRIVATE(submsg) = NULL}");
101 bool IsCrossFile(upb::FieldDefPtr field
) {
102 return field
.message_type() != field
.containing_type();
105 // Writes a single message into a .upb.c source file.
106 void WriteMessage(upb::MessageDefPtr message
, const DefPoolPair
& pools
,
107 const MiniTableOptions
& options
, Output
& output
) {
108 std::string fields_array_ref
= "NULL";
109 std::string submsgs_array_ref
= "NULL";
110 std::string subenums_array_ref
= "NULL";
111 const upb_MiniTable
* mt_32
= pools
.GetMiniTable32(message
);
112 const upb_MiniTable
* mt_64
= pools
.GetMiniTable64(message
);
113 std::map
<int, std::string
> subs
;
114 absl::flat_hash_set
<const upb_MiniTable
*> seen
;
116 // Construct map of sub messages by field number.
117 for (int i
= 0; i
< mt_64
->UPB_PRIVATE(field_count
); i
++) {
118 const upb_MiniTableField
* f
= &mt_64
->UPB_PRIVATE(fields
)[i
];
119 uint32_t index
= f
->UPB_PRIVATE(submsg_index
);
120 if (index
!= kUpb_NoSub
) {
121 const int f_number
= upb_MiniTableField_Number(f
);
122 upb::FieldDefPtr field
= message
.FindFieldByNumber(f_number
);
123 auto pair
= subs
.emplace(index
, GetSub(field
, false));
124 ABSL_CHECK(pair
.second
);
125 if (options
.one_output_per_message
&& field
.IsSubMessage() &&
126 IsCrossFile(field
) && !upb_MiniTableField_IsMap(f
)) {
127 if (seen
.insert(pools
.GetMiniTable64(field
.message_type())).second
) {
129 "__attribute__((weak)) const upb_MiniTable* $0 ="
130 " &UPB_PRIVATE(_kUpb_MiniTable_StaticallyTreeShaken);\n",
131 MessagePtrVarName(field
.message_type()));
136 // Write upb_MiniTableSubInternal table for sub messages referenced from
139 std::string submsgs_array_name
=
140 MiniTableSubMessagesVarName(message
.full_name());
141 submsgs_array_ref
= "&" + submsgs_array_name
+ "[0]";
142 output("static const upb_MiniTableSubInternal $0[$1] = {\n",
143 submsgs_array_name
, subs
.size());
146 for (const auto& pair
: subs
) {
147 ABSL_CHECK(pair
.first
== i
++);
148 output(" $0,\n", pair
.second
);
154 // Write upb_MiniTableField table.
155 if (mt_64
->UPB_PRIVATE(field_count
) > 0) {
156 std::string fields_array_name
= MiniTableFieldsVarName(message
.full_name());
157 fields_array_ref
= "&" + fields_array_name
+ "[0]";
158 output("static const upb_MiniTableField $0[$1] = {\n", fields_array_name
,
159 mt_64
->UPB_PRIVATE(field_count
));
160 for (int i
= 0; i
< mt_64
->UPB_PRIVATE(field_count
); i
++) {
161 WriteMessageField(message
.FindFieldByNumber(
162 mt_64
->UPB_PRIVATE(fields
)[i
].UPB_PRIVATE(number
)),
163 &mt_64
->UPB_PRIVATE(fields
)[i
],
164 &mt_32
->UPB_PRIVATE(fields
)[i
], output
);
169 std::vector
<TableEntry
> table
;
170 uint8_t table_mask
= ~0;
172 table
= FastDecodeTable(message
, pools
);
174 if (table
.size() > 1) {
175 UPB_ASSERT((table
.size() & (table
.size() - 1)) == 0);
176 table_mask
= (table
.size() - 1) << 3;
179 std::string msgext
= "kUpb_ExtMode_NonExtendable";
181 if (message
.extension_range_count()) {
182 if (UPB_DESC(MessageOptions_message_set_wire_format
)(message
.options())) {
183 msgext
= "kUpb_ExtMode_IsMessageSet";
185 msgext
= "kUpb_ExtMode_Extendable";
189 output("const upb_MiniTable $0 = {\n", MessageVarName(message
));
190 output(" $0,\n", submsgs_array_ref
);
191 output(" $0,\n", fields_array_ref
);
192 output(" $0, $1, $2, $3, UPB_FASTTABLE_MASK($4), $5,\n",
193 ArchDependentSize(mt_32
->UPB_PRIVATE(size
), mt_64
->UPB_PRIVATE(size
)),
194 mt_64
->UPB_PRIVATE(field_count
), msgext
,
195 mt_64
->UPB_PRIVATE(dense_below
), table_mask
,
196 mt_64
->UPB_PRIVATE(required_count
));
197 output("#ifdef UPB_TRACING_ENABLED\n");
198 output(" \"$0\",\n", message
.full_name());
200 if (!table
.empty()) {
201 output(" UPB_FASTTABLE_INIT({\n");
202 for (const auto& ent
: table
) {
203 output(" {0x$1, &$0},\n", ent
.first
,
204 absl::StrCat(absl::Hex(ent
.second
, absl::kZeroPad16
)));
209 output("const upb_MiniTable* $0 = &$1;\n", MessagePtrVarName(message
),
210 MessageVarName(message
));
213 void WriteEnum(upb::EnumDefPtr e
, Output
& output
) {
214 std::string values_init
= "{\n";
215 const upb_MiniTableEnum
* mt
= e
.mini_table();
216 uint32_t value_count
=
217 (mt
->UPB_PRIVATE(mask_limit
) / 32) + mt
->UPB_PRIVATE(value_count
);
218 for (uint32_t i
= 0; i
< value_count
; i
++) {
219 absl::StrAppend(&values_init
, " 0x",
220 absl::Hex(mt
->UPB_PRIVATE(data
)[i
]), ",\n");
226 const upb_MiniTableEnum $0 = {
232 EnumVarName(e
), mt
->UPB_PRIVATE(mask_limit
), mt
->UPB_PRIVATE(value_count
),
237 void WriteExtension(const DefPoolPair
& pools
, upb::FieldDefPtr ext
,
239 output("UPB_LINKARR_APPEND(upb_AllExts)\n");
240 output("const upb_MiniTableExtension $0 = {\n ", ExtensionVarName(ext
));
241 output("$0,\n", FieldInitializer(pools
, ext
));
242 output(" &$0,\n", MessageVarName(ext
.containing_type()));
243 output(" $0,\n", GetSub(ext
, true));
249 void WriteMiniTableHeader(const DefPoolPair
& pools
, upb::FileDefPtr file
,
250 const MiniTableOptions
& options
, Output
& output
) {
251 output(FileWarning(file
.name()));
253 "#ifndef $0_UPB_MINITABLE_H_\n"
254 "#define $0_UPB_MINITABLE_H_\n\n"
255 "#include \"upb/generated_code_support.h\"\n",
256 IncludeGuard(file
.name()));
258 for (int i
= 0; i
< file
.public_dependency_count(); i
++) {
260 output("/* Public Imports. */\n");
262 output("#include \"$0\"\n",
263 HeaderFilename(file
.public_dependency(i
), options
.bootstrap
));
264 if (i
== file
.public_dependency_count() - 1) {
272 "#include \"upb/port/def.inc\"\n"
274 "#ifdef __cplusplus\n"
279 const std::vector
<upb::MessageDefPtr
> this_file_messages
=
280 SortedMessages(file
);
281 const std::vector
<upb::FieldDefPtr
> this_file_exts
= SortedExtensions(file
);
283 for (auto message
: this_file_messages
) {
284 output("extern const upb_MiniTable $0;\n", MessageVarName(message
));
285 output("extern const upb_MiniTable* $0;\n", MessagePtrVarName(message
));
287 for (auto ext
: this_file_exts
) {
288 output("extern const upb_MiniTableExtension $0;\n", ExtensionVarName(ext
));
293 std::vector
<upb::EnumDefPtr
> this_file_enums
=
294 SortedEnums(file
, kClosedEnums
);
296 for (const auto enumdesc
: this_file_enums
) {
297 output("extern const upb_MiniTableEnum $0;\n", EnumVarName(enumdesc
));
300 output("extern const upb_MiniTableFile $0;\n\n", FileVarName(file
));
303 "#ifdef __cplusplus\n"
304 "} /* extern \"C\" */\n"
307 "#include \"upb/port/undef.inc\"\n"
309 "#endif /* $0_UPB_MINITABLE_H_ */\n",
310 IncludeGuard(file
.name()));
313 void WriteMiniTableSourceIncludes(upb::FileDefPtr file
,
314 const MiniTableOptions
& options
,
316 output(FileWarning(file
.name()));
319 "#include <stddef.h>\n"
320 "#include \"upb/generated_code_support.h\"\n"
322 HeaderFilename(file
, options
.bootstrap
));
324 for (int i
= 0; i
< file
.dependency_count(); i
++) {
325 if (options
.strip_nonfunctional_codegen
&&
326 google::protobuf::compiler::IsKnownFeatureProto(file
.dependency(i
).name())) {
327 // Strip feature imports for editions codegen tests.
330 output("#include \"$0\"\n",
331 HeaderFilename(file
.dependency(i
), options
.bootstrap
));
337 "#include \"upb/port/def.inc\"\n"
341 "extern const struct upb_MiniTable "
342 "UPB_PRIVATE(_kUpb_MiniTable_StaticallyTreeShaken);\n");
345 void WriteMiniTableSource(const DefPoolPair
& pools
, upb::FileDefPtr file
,
346 const MiniTableOptions
& options
, Output
& output
) {
347 WriteMiniTableSourceIncludes(file
, options
, output
);
349 std::vector
<upb::MessageDefPtr
> messages
= SortedMessages(file
);
350 std::vector
<upb::FieldDefPtr
> extensions
= SortedExtensions(file
);
351 std::vector
<upb::EnumDefPtr
> enums
= SortedEnums(file
, kClosedEnums
);
353 if (options
.one_output_per_message
) {
354 for (auto message
: messages
) {
355 output("extern const upb_MiniTable* $0;\n", MessagePtrVarName(message
));
357 for (const auto e
: enums
) {
358 output("extern const upb_MiniTableEnum $0;\n", EnumVarName(e
));
360 for (const auto ext
: extensions
) {
361 output("extern const upb_MiniTableExtension $0;\n",
362 ExtensionVarName(ext
));
365 for (auto message
: messages
) {
366 WriteMessage(message
, pools
, options
, output
);
368 for (const auto e
: enums
) {
369 WriteEnum(e
, output
);
371 for (const auto ext
: extensions
) {
372 WriteExtension(pools
, ext
, output
);
377 if (!messages
.empty()) {
378 output("static const upb_MiniTable *$0[$1] = {\n", kMessagesInit
,
380 for (auto message
: messages
) {
381 output(" &$0,\n", MessageVarName(message
));
388 if (!enums
.empty()) {
389 output("static const upb_MiniTableEnum *$0[$1] = {\n", kEnumsInit
,
391 for (const auto e
: enums
) {
392 output(" &$0,\n", EnumVarName(e
));
398 if (!extensions
.empty()) {
402 "static const upb_MiniTableExtension *$0[$1] = {\n",
403 kExtensionsInit
, extensions
.size());
405 for (auto ext
: extensions
) {
406 output(" &$0,\n", ExtensionVarName(ext
));
414 output("const upb_MiniTableFile $0 = {\n", FileVarName(file
));
415 output(" $0,\n", messages
.empty() ? "NULL" : kMessagesInit
);
416 output(" $0,\n", enums
.empty() ? "NULL" : kEnumsInit
);
417 output(" $0,\n", extensions
.empty() ? "NULL" : kExtensionsInit
);
418 output(" $0,\n", messages
.size());
419 output(" $0,\n", enums
.size());
420 output(" $0,\n", extensions
.size());
423 output("#include \"upb/port/undef.inc\"\n");
427 std::string
MultipleSourceFilename(upb::FileDefPtr file
,
428 absl::string_view full_name
, int* i
) {
430 return absl::StrCat(StripExtension(file
.name()), ".upb_weak_minitables/",
434 void WriteMiniTableMultipleSources(const DefPoolPair
& pools
,
435 upb::FileDefPtr file
,
436 const MiniTableOptions
& options
,
438 std::vector
<upb::MessageDefPtr
> messages
= SortedMessages(file
);
439 std::vector
<upb::FieldDefPtr
> extensions
= SortedExtensions(file
);
440 std::vector
<upb::EnumDefPtr
> enums
= SortedEnums(file
, kClosedEnums
);
443 for (auto message
: messages
) {
445 WriteMiniTableSourceIncludes(file
, options
, output
);
446 WriteMessage(message
, pools
, options
, output
);
447 plugin
->AddOutputFile(MultipleSourceFilename(file
, message
.full_name(), &i
),
450 for (const auto e
: enums
) {
452 WriteMiniTableSourceIncludes(file
, options
, output
);
453 WriteEnum(e
, output
);
454 plugin
->AddOutputFile(MultipleSourceFilename(file
, e
.full_name(), &i
),
457 for (const auto ext
: extensions
) {
459 WriteMiniTableSourceIncludes(file
, options
, output
);
460 WriteExtension(pools
, ext
, output
);
461 plugin
->AddOutputFile(MultipleSourceFilename(file
, ext
.full_name(), &i
),
466 } // namespace generator