1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC. All rights reserved.
3 //
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
7
8 #include "google/protobuf/compiler/hpb/gen_messages.h"
9
10 #include <cstddef>
11 #include <string>
12 #include <vector>
13
14 #include "google/protobuf/descriptor.pb.h"
15 #include "absl/strings/ascii.h"
16 #include "absl/strings/str_cat.h"
17 #include "absl/strings/string_view.h"
18 #include "google/protobuf/compiler/hpb/gen_accessors.h"
19 #include "google/protobuf/compiler/hpb/gen_enums.h"
20 #include "google/protobuf/compiler/hpb/gen_extensions.h"
21 #include "google/protobuf/compiler/hpb/gen_utils.h"
22 #include "google/protobuf/compiler/hpb/names.h"
23 #include "google/protobuf/compiler/hpb/output.h"
24 #include "google/protobuf/descriptor.h"
25 #include "upb_generator/minitable/names.h"
26
27 namespace google::protobuf::hpb_generator {
28
29 namespace protobuf = ::proto2;
30
31 void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
32 Output& output);
33 void WriteModelPublicDeclaration(
34 const protobuf::Descriptor* descriptor,
35 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
36 const std::vector<const protobuf::EnumDescriptor*>& file_enums,
37 Output& output);
38 void WriteExtensionIdentifiersInClassHeader(
39 const protobuf::Descriptor* message,
40 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
41 Output& output);
42 void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
43 Output& output);
44 void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
45 Output& output);
46 void WriteInternalForwardDeclarationsInHeader(
47 const protobuf::Descriptor* message, Output& output);
48 void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
49 Output& output);
50 void WriteExtensionIdentifiersImplementation(
51 const protobuf::Descriptor* message,
52 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
53 Output& output);
54 void WriteUsingEnumsInHeader(
55 const protobuf::Descriptor* message,
56 const std::vector<const protobuf::EnumDescriptor*>& file_enums,
57 Output& output);
58
59 // Writes message class declarations into .upb.proto.h.
60 //
61 // For each proto Foo, FooAccess and FooProxy/FooCProxy are generated
62 // that are exposed to users as Foo , Ptr<Foo> and Ptr<const Foo>.
WriteMessageClassDeclarations(const protobuf::Descriptor * descriptor,const std::vector<const protobuf::FieldDescriptor * > & file_exts,const std::vector<const protobuf::EnumDescriptor * > & file_enums,Output & output)63 void WriteMessageClassDeclarations(
64 const protobuf::Descriptor* descriptor,
65 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
66 const std::vector<const protobuf::EnumDescriptor*>& file_enums,
67 Output& output) {
68 if (IsMapEntryMessage(descriptor)) {
69 // Skip map entry generation. Low level accessors for maps are
70 // generated that don't require a separate map type.
71 return;
72 }
73
74 // Forward declaration of Proto Class for GCC handling of free friend method.
75 output("class $0;\n", ClassName(descriptor));
76 output("namespace internal {\n\n");
77 WriteModelAccessDeclaration(descriptor, output);
78 output("\n");
79 WriteInternalForwardDeclarationsInHeader(descriptor, output);
80 output("\n");
81 output("} // namespace internal\n\n");
82 WriteModelPublicDeclaration(descriptor, file_exts, file_enums, output);
83 output("namespace internal {\n");
84 WriteModelCProxyDeclaration(descriptor, output);
85 WriteModelProxyDeclaration(descriptor, output);
86 output("} // namespace internal\n\n");
87 }
88
WriteModelAccessDeclaration(const protobuf::Descriptor * descriptor,Output & output)89 void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
90 Output& output) {
91 output(
92 R"cc(
93 class $0Access {
94 public:
95 $0Access() {}
96 $0Access($1* msg, upb_Arena* arena) : msg_(msg), arena_(arena) {
97 assert(arena != nullptr);
98 } // NOLINT
99 $0Access(const $1* msg, upb_Arena* arena)
100 : msg_(const_cast<$1*>(msg)), arena_(arena) {
101 assert(arena != nullptr);
102 } // NOLINT
103 )cc",
104 ClassName(descriptor), MessageName(descriptor));
105 WriteFieldAccessorsInHeader(descriptor, output);
106 WriteOneofAccessorsInHeader(descriptor, output);
107 output.Indent();
108 output(
109 R"cc(
110 private:
111 friend class $2;
112 friend class $0Proxy;
113 friend class $0CProxy;
114 friend struct ::hpb::internal::PrivateAccess;
115 $1* msg_;
116 upb_Arena* arena_;
117 )cc",
118 ClassName(descriptor), MessageName(descriptor),
119 QualifiedClassName(descriptor));
120 output.Outdent();
121 output("};\n");
122 }
123
UnderscoresToCamelCase(absl::string_view input,bool cap_next_letter)124 std::string UnderscoresToCamelCase(absl::string_view input,
125 bool cap_next_letter) {
126 std::string result;
127
128 for (size_t i = 0; i < input.size(); i++) {
129 if (absl::ascii_islower(input[i])) {
130 if (cap_next_letter) {
131 result += absl::ascii_toupper(input[i]);
132 } else {
133 result += input[i];
134 }
135 cap_next_letter = false;
136 } else if (absl::ascii_isupper(input[i])) {
137 // Capital letters are left as-is.
138 result += input[i];
139 cap_next_letter = false;
140 } else if (absl::ascii_isdigit(input[i])) {
141 result += input[i];
142 cap_next_letter = true;
143 } else {
144 cap_next_letter = true;
145 }
146 }
147 return result;
148 }
149
FieldConstantName(const protobuf::FieldDescriptor * field)150 std::string FieldConstantName(const protobuf::FieldDescriptor* field) {
151 std::string field_name = UnderscoresToCamelCase(field->name(), true);
152 std::string result = absl::StrCat("k", field_name, "FieldNumber");
153
154 if (!field->is_extension() &&
155 field->containing_type()->FindFieldByCamelcaseName(
156 field->camelcase_name()) != field) {
157 // This field's camelcase name is not unique, add field number to make it
158 // unique.
159 absl::StrAppend(&result, "_", field->number());
160 }
161 return result;
162 }
163
WriteConstFieldNumbers(Output & output,const protobuf::Descriptor * descriptor)164 void WriteConstFieldNumbers(Output& output,
165 const protobuf::Descriptor* descriptor) {
166 for (auto field : FieldRange(descriptor)) {
167 output("static constexpr ::uint32_t $0 = $1;\n", FieldConstantName(field),
168 field->number());
169 }
170 output("\n\n");
171 }
172
WriteModelPublicDeclaration(const protobuf::Descriptor * descriptor,const std::vector<const protobuf::FieldDescriptor * > & file_exts,const std::vector<const protobuf::EnumDescriptor * > & file_enums,Output & output)173 void WriteModelPublicDeclaration(
174 const protobuf::Descriptor* descriptor,
175 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
176 const std::vector<const protobuf::EnumDescriptor*>& file_enums,
177 Output& output) {
178 output(
179 R"cc(
180 class $0 final : private internal::$0Access {
181 public:
182 using Access = internal::$0Access;
183 using Proxy = internal::$0Proxy;
184 using CProxy = internal::$0CProxy;
185
186 $0();
187
188 $0(const $0& from);
189 $0& operator=(const $3& from);
190 $0(const CProxy& from);
191 $0(const Proxy& from);
192 $0& operator=(const CProxy& from);
193
194 $0($0&& m)
195 : Access(std::exchange(m.msg_, nullptr),
196 std::exchange(m.arena_, nullptr)),
197 owned_arena_(std::move(m.owned_arena_)) {}
198
199 $0& operator=($0&& m) {
200 msg_ = std::exchange(m.msg_, nullptr);
201 arena_ = std::exchange(m.arena_, nullptr);
202 owned_arena_ = std::move(m.owned_arena_);
203 return *this;
204 }
205 )cc",
206 ClassName(descriptor),
207 ::upb::generator::MiniTableMessageVarName(descriptor->full_name()),
208 MessageName(descriptor), QualifiedClassName(descriptor));
209
210 WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output);
211 WriteUsingEnumsInHeader(descriptor, file_enums, output);
212 WriteDefaultInstanceHeader(descriptor, output);
213 WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output);
214 if (descriptor->extension_range_count()) {
215 // for typetrait checking
216 output("using ExtendableType = $0;\n", ClassName(descriptor));
217 }
218 // Note: free function friends that are templates such as ::hpb::Parse
219 // require explicit <$2> type parameter in declaration to be able to compile
220 // with gcc otherwise the compiler will fail with
221 // "has not been declared within namespace" error. Even though there is a
222 // namespace qualifier, cross namespace matching fails.
223 output.Indent();
224 output(
225 R"cc(
226 static const upb_MiniTable* minitable();
227 )cc",
228 ClassName(descriptor));
229 output("\n");
230 WriteConstFieldNumbers(output, descriptor);
231 output(
232 R"cc(
233 private:
234 const upb_Message* msg() const { return UPB_UPCAST(msg_); }
235 upb_Message* msg() { return UPB_UPCAST(msg_); }
236
237 upb_Arena* arena() const { return arena_; }
238
239 $0(upb_Message* msg, upb_Arena* arena) : $0Access() {
240 msg_ = ($1*)msg;
241 arena_ = owned_arena_.ptr();
242 upb_Arena_Fuse(arena_, arena);
243 }
244 ::hpb::Arena owned_arena_;
245 friend struct ::hpb::internal::PrivateAccess;
246 friend Proxy;
247 friend CProxy;
248 friend absl::StatusOr<$2>(::hpb::Parse<$2>(absl::string_view bytes,
249 int options));
250 friend absl::StatusOr<$2>(::hpb::Parse<$2>(
251 absl::string_view bytes,
252 const ::hpb::ExtensionRegistry& extension_registry, int options));
253 friend upb_Arena* hpb::interop::upb::GetArena<$0>($0* message);
254 friend upb_Arena* hpb::interop::upb::GetArena<$0>(::hpb::Ptr<$0> message);
255 friend $0(hpb::interop::upb::MoveMessage<$0>(upb_Message* msg,
256 upb_Arena* arena));
257 )cc",
258 ClassName(descriptor), MessageName(descriptor),
259 QualifiedClassName(descriptor));
260 output.Outdent();
261 output("};\n\n");
262 }
263
WriteModelProxyDeclaration(const protobuf::Descriptor * descriptor,Output & output)264 void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
265 Output& output) {
266 // Foo::Proxy.
267 output(
268 R"cc(
269 class $0Proxy final : private internal::$0Access {
270 public:
271 $0Proxy() = delete;
272 $0Proxy(const $0Proxy& m) : internal::$0Access() {
273 msg_ = m.msg_;
274 arena_ = m.arena_;
275 }
276 $0Proxy($0* m) : internal::$0Access() {
277 msg_ = m->msg_;
278 arena_ = m->arena_;
279 }
280 $0Proxy operator=(const $0Proxy& m) {
281 msg_ = m.msg_;
282 arena_ = m.arena_;
283 return *this;
284 }
285 )cc",
286 ClassName(descriptor));
287
288 WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
289 output);
290 output("\n");
291 output.Indent(1);
292 output(
293 R"cc(
294 private:
295 upb_Message* msg() const { return UPB_UPCAST(msg_); }
296
297 upb_Arena* arena() const { return arena_; }
298
299 $0Proxy(upb_Message* msg, upb_Arena* arena)
300 : internal::$0Access(($1*)msg, arena) {}
301 friend $0::Proxy(::hpb::CreateMessage<$0>(::hpb::Arena& arena));
302 friend $0::Proxy(hpb::interop::upb::MakeHandle<$0>(upb_Message*, upb_Arena*));
303 friend struct ::hpb::internal::PrivateAccess;
304 friend class RepeatedFieldProxy;
305 friend class $0CProxy;
306 friend class $0Access;
307 friend class ::hpb::Ptr<$0>;
308 friend class ::hpb::Ptr<const $0>;
309 static const upb_MiniTable* minitable() { return $0::minitable(); }
310 friend const upb_MiniTable* ::hpb::interop::upb::GetMiniTable<$0Proxy>(
311 const $0Proxy* message);
312 friend const upb_MiniTable* ::hpb::interop::upb::GetMiniTable<$0Proxy>(
313 ::hpb::Ptr<$0Proxy> message);
314 friend upb_Arena* hpb::interop::upb::GetArena<$2>($2* message);
315 friend upb_Arena* hpb::interop::upb::GetArena<$2>(::hpb::Ptr<$2> message);
316 static void Rebind($0Proxy& lhs, const $0Proxy& rhs) {
317 lhs.msg_ = rhs.msg_;
318 lhs.arena_ = rhs.arena_;
319 }
320 )cc",
321 ClassName(descriptor), MessageName(descriptor),
322 QualifiedClassName(descriptor));
323 output.Outdent(1);
324 output("};\n\n");
325 }
326
WriteModelCProxyDeclaration(const protobuf::Descriptor * descriptor,Output & output)327 void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
328 Output& output) {
329 // Foo::CProxy.
330 output(
331 R"cc(
332 class $0CProxy final : private internal::$0Access {
333 public:
334 $0CProxy() = delete;
335 $0CProxy(const $0* m)
336 : internal::$0Access(m->msg_, hpb::interop::upb::GetArena(m)) {}
337 $0CProxy($0Proxy m);
338 )cc",
339 ClassName(descriptor), MessageName(descriptor));
340
341 WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageCProxy,
342 output);
343
344 output.Indent(1);
345 output(
346 R"cc(
347 private:
348 using AsNonConst = $0Proxy;
349 const upb_Message* msg() const { return UPB_UPCAST(msg_); }
350 upb_Arena* arena() const { return arena_; }
351
352 $0CProxy(const upb_Message* msg, upb_Arena* arena)
353 : internal::$0Access(($1*)msg, arena){};
354 friend struct ::hpb::internal::PrivateAccess;
355 friend class RepeatedFieldProxy;
356 friend class ::hpb::Ptr<$0>;
357 friend class ::hpb::Ptr<const $0>;
358 static const upb_MiniTable* minitable() { return $0::minitable(); }
359 friend const upb_MiniTable* ::hpb::interop::upb::GetMiniTable<$0CProxy>(
360 const $0CProxy* message);
361 friend const upb_MiniTable* ::hpb::interop::upb::GetMiniTable<$0CProxy>(
362 ::hpb::Ptr<$0CProxy> message);
363
364 static void Rebind($0CProxy& lhs, const $0CProxy& rhs) {
365 lhs.msg_ = rhs.msg_;
366 lhs.arena_ = rhs.arena_;
367 }
368 )cc",
369 ClassName(descriptor), MessageName(descriptor));
370 output.Outdent(1);
371 output("};\n\n");
372 }
373
WriteDefaultInstanceHeader(const protobuf::Descriptor * message,Output & output)374 void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
375 Output& output) {
376 output(" static ::hpb::Ptr<const $0> default_instance();\n",
377 ClassName(message));
378 }
379
WriteMessageImplementation(const protobuf::Descriptor * descriptor,const std::vector<const protobuf::FieldDescriptor * > & file_exts,Output & output)380 void WriteMessageImplementation(
381 const protobuf::Descriptor* descriptor,
382 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
383 Output& output) {
384 bool message_is_map_entry = descriptor->options().map_entry();
385 if (!message_is_map_entry) {
386 // Constructor.
387 output(
388 R"cc(
389 $0::$0() : $0Access() {
390 arena_ = owned_arena_.ptr();
391 msg_ = $1_new(arena_);
392 }
393 $0::$0(const $0& from) : $0Access() {
394 arena_ = owned_arena_.ptr();
395 msg_ = ($1*)::hpb::internal::DeepClone(UPB_UPCAST(from.msg_), &$2, arena_);
396 }
397 $0::$0(const CProxy& from) : $0Access() {
398 arena_ = owned_arena_.ptr();
399 msg_ = ($1*)::hpb::internal::DeepClone(
400 ::hpb::interop::upb::GetMessage(&from), &$2, arena_);
401 }
402 $0::$0(const Proxy& from) : $0(static_cast<const CProxy&>(from)) {}
403 internal::$0CProxy::$0CProxy($0Proxy m) : $0Access() {
404 arena_ = m.arena_;
405 msg_ = ($1*)::hpb::interop::upb::GetMessage(&m);
406 }
407 $0& $0::operator=(const $3& from) {
408 arena_ = owned_arena_.ptr();
409 msg_ = ($1*)::hpb::internal::DeepClone(UPB_UPCAST(from.msg_), &$2, arena_);
410 return *this;
411 }
412 $0& $0::operator=(const CProxy& from) {
413 arena_ = owned_arena_.ptr();
414 msg_ = ($1*)::hpb::internal::DeepClone(
415 ::hpb::interop::upb::GetMessage(&from), &$2, arena_);
416 return *this;
417 }
418 )cc",
419 ClassName(descriptor), MessageName(descriptor),
420 ::upb::generator::MiniTableMessageVarName(descriptor->full_name()),
421 QualifiedClassName(descriptor));
422 output("\n");
423 // Minitable
424 output(
425 R"cc(
426 const upb_MiniTable* $0::minitable() { return &$1; }
427 )cc",
428 ClassName(descriptor),
429 ::upb::generator::MiniTableMessageVarName(descriptor->full_name()));
430 output("\n");
431 }
432
433 WriteAccessorsInSource(descriptor, output);
434
435 if (!message_is_map_entry) {
436 output(
437 R"cc(
438 struct $0DefaultTypeInternal {
439 $1* msg;
440 upb_Arena* arena;
441 };
442 static $0DefaultTypeInternal _$0DefaultTypeBuilder() {
443 upb_Arena* arena = upb_Arena_New();
444 return $0DefaultTypeInternal{$1_new(arena), arena};
445 }
446 $0DefaultTypeInternal _$0_default_instance_ = _$0DefaultTypeBuilder();
447 )cc",
448 ClassName(descriptor), MessageName(descriptor));
449
450 output(
451 R"cc(
452 ::hpb::Ptr<const $0> $0::default_instance() {
453 return ::hpb::interop::upb::MakeCHandle<$0>(
454 (upb_Message *)_$0_default_instance_.msg,
455 _$0_default_instance_.arena);
456 }
457 )cc",
458 ClassName(descriptor));
459
460 WriteExtensionIdentifiersImplementation(descriptor, file_exts, output);
461 }
462 }
463
WriteInternalForwardDeclarationsInHeader(const protobuf::Descriptor * message,Output & output)464 void WriteInternalForwardDeclarationsInHeader(
465 const protobuf::Descriptor* message, Output& output) {
466 // Write declaration for internal re-usable default_instance without
467 // leaking implementation.
468 output(
469 R"cc(
470 struct $0DefaultTypeInternal;
471 extern $0DefaultTypeInternal _$0_default_instance_;
472 )cc",
473 ClassName(message));
474 }
475
WriteExtensionIdentifiersInClassHeader(const protobuf::Descriptor * message,const std::vector<const protobuf::FieldDescriptor * > & file_exts,Output & output)476 void WriteExtensionIdentifiersInClassHeader(
477 const protobuf::Descriptor* message,
478 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
479 Output& output) {
480 for (auto* ext : file_exts) {
481 if (ext->extension_scope() &&
482 ext->extension_scope()->full_name() == message->full_name()) {
483 WriteExtensionIdentifierHeader(ext, output);
484 }
485 }
486 }
487
WriteExtensionIdentifiersImplementation(const protobuf::Descriptor * message,const std::vector<const protobuf::FieldDescriptor * > & file_exts,Output & output)488 void WriteExtensionIdentifiersImplementation(
489 const protobuf::Descriptor* message,
490 const std::vector<const protobuf::FieldDescriptor*>& file_exts,
491 Output& output) {
492 for (auto* ext : file_exts) {
493 if (ext->extension_scope() &&
494 ext->extension_scope()->full_name() == message->full_name()) {
495 WriteExtensionIdentifier(ext, output);
496 }
497 }
498 }
499
WriteUsingEnumsInHeader(const protobuf::Descriptor * message,const std::vector<const protobuf::EnumDescriptor * > & file_enums,Output & output)500 void WriteUsingEnumsInHeader(
501 const protobuf::Descriptor* message,
502 const std::vector<const protobuf::EnumDescriptor*>& file_enums,
503 Output& output) {
504 for (auto* enum_descriptor : file_enums) {
505 std::string enum_type_name = EnumTypeName(enum_descriptor);
506 std::string enum_resolved_type_name =
507 enum_descriptor->file()->package().empty() &&
508 enum_descriptor->containing_type() == nullptr
509 ? absl::StrCat(kNoPackageNamePrefix,
510 ToCIdent(enum_descriptor->name()))
511 : enum_type_name;
512 if (enum_descriptor->containing_type() == nullptr ||
513 enum_descriptor->containing_type()->full_name() !=
514 message->full_name()) {
515 continue;
516 }
517 output("using $0", enum_descriptor->name());
518 if (enum_descriptor->options().deprecated()) {
519 output(" ABSL_DEPRECATED(\"Proto enum $0\")", enum_descriptor->name());
520 }
521 output(" = $0;", enum_resolved_type_name);
522 output("\n");
523 int value_count = enum_descriptor->value_count();
524 for (int i = 0; i < value_count; i++) {
525 output("static constexpr $0 $1", enum_descriptor->name(),
526 enum_descriptor->value(i)->name());
527 if (enum_descriptor->options().deprecated() ||
528 enum_descriptor->value(i)->options().deprecated()) {
529 output(" ABSL_DEPRECATED(\"Proto enum value $0\") ",
530 enum_descriptor->value(i)->name());
531 }
532 output(" = $0;\n", EnumValueSymbolInNameSpace(enum_descriptor,
533 enum_descriptor->value(i)));
534 }
535 }
536 }
537
538 } // namespace protobuf
539 } // namespace google::hpb_generator
540