• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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