1 /*
2  * Copyright (c) 2009-2021, Google LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of Google LLC nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "protos_generator/gen_messages.h"
29 
30 #include <string>
31 #include <vector>
32 
33 #include "google/protobuf/descriptor.pb.h"
34 #include "google/protobuf/descriptor.h"
35 #include "protos_generator/gen_accessors.h"
36 #include "protos_generator/gen_enums.h"
37 #include "protos_generator/gen_extensions.h"
38 #include "protos_generator/gen_utils.h"
39 #include "protos_generator/output.h"
40 #include "upbc/common.h"
41 #include "upbc/file_layout.h"
42 
43 namespace protos_generator {
44 
45 namespace protobuf = ::google::protobuf;
46 
47 void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
48                                  Output& output);
49 void WriteModelPublicDeclaration(
50     const protobuf::Descriptor* descriptor,
51     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
52     const std::vector<const protobuf::EnumDescriptor*>& file_enums,
53     Output& output);
54 void WriteExtensionIdentifiersInClassHeader(
55     const protobuf::Descriptor* message,
56     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
57     Output& output);
58 void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
59                                 Output& output);
60 void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
61                                  Output& output);
62 void WriteInternalForwardDeclarationsInHeader(
63     const protobuf::Descriptor* message, Output& output);
64 void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
65                                 Output& output);
66 void WriteExtensionIdentifiersImplementation(
67     const protobuf::Descriptor* message,
68     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
69     Output& output);
70 void WriteUsingEnumsInHeader(
71     const protobuf::Descriptor* message,
72     const std::vector<const protobuf::EnumDescriptor*>& file_enums,
73     Output& output);
74 
75 // Writes message class declarations into .upb.proto.h.
76 //
77 // For each proto Foo, FooAccess and FooProxy/FooCProxy are generated
78 // 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)79 void WriteMessageClassDeclarations(
80     const protobuf::Descriptor* descriptor,
81     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
82     const std::vector<const protobuf::EnumDescriptor*>& file_enums,
83     Output& output) {
84   if (IsMapEntryMessage(descriptor)) {
85     // Skip map entry generation. Low level accessors for maps are
86     // generated that don't require a separate map type.
87     return;
88   }
89 
90   // Forward declaration of Proto Class for GCC handling of free friend method.
91   output("class $0;", ClassName(descriptor));
92   output("namespace internal {\n");
93   WriteModelAccessDeclaration(descriptor, output);
94   output("\n");
95   WriteInternalForwardDeclarationsInHeader(descriptor, output);
96   output("\n");
97   output("}  // namespace internal\n");
98   WriteModelPublicDeclaration(descriptor, file_exts, file_enums, output);
99   output("namespace internal {\n");
100   WriteModelCProxyDeclaration(descriptor, output);
101   WriteModelProxyDeclaration(descriptor, output);
102   output("}  // namespace internal\n");
103 }
104 
WriteModelAccessDeclaration(const protobuf::Descriptor * descriptor,Output & output)105 void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
106                                  Output& output) {
107   output(
108       R"cc(
109 
110         class $0Access {
111          public:
112           $0Access() {}
113           $0Access($1* msg, upb_Arena* arena) : msg_(msg), arena_(arena) {}  // NOLINT
114           $0Access(const $1* msg, upb_Arena* arena)
115               : msg_(const_cast<$1*>(msg)), arena_(arena) {}  // NOLINT
116           void* GetInternalArena() const { return arena_; }
117       )cc",
118       ClassName(descriptor), MessageName(descriptor));
119   WriteFieldAccessorsInHeader(descriptor, output);
120   WriteOneofAccessorsInHeader(descriptor, output);
121   output.Indent();
122   output(
123       R"cc(
124         private:
125         void* msg() const { return msg_; }
126 
127         friend class $2;
128         friend class $0Proxy;
129         friend class $0CProxy;
130         friend void* ::protos::internal::GetInternalMsg<$2>(const $2& message);
131         friend void* ::protos::internal::GetInternalMsg<$2>(
132             const ::protos::Ptr<$2>& message);
133         $1* msg_;
134         upb_Arena* arena_;
135       )cc",
136       ClassName(descriptor), MessageName(descriptor),
137       QualifiedClassName(descriptor));
138   output.Outdent();
139   output("};\n");
140 }
141 
WriteModelPublicDeclaration(const protobuf::Descriptor * descriptor,const std::vector<const protobuf::FieldDescriptor * > & file_exts,const std::vector<const protobuf::EnumDescriptor * > & file_enums,Output & output)142 void WriteModelPublicDeclaration(
143     const protobuf::Descriptor* descriptor,
144     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
145     const std::vector<const protobuf::EnumDescriptor*>& file_enums,
146     Output& output) {
147   output(
148       R"cc(
149         class $0 final : private internal::$0Access {
150          public:
151           using Access = internal::$0Access;
152           using Proxy = internal::$0Proxy;
153           using CProxy = internal::$0CProxy;
154 
155           $0();
156 
157           $0(const $0& from);
158           inline $0& operator=(const $3& from) {
159             arena_ = owned_arena_.ptr();
160             msg_ = ($2*)upb_Message_DeepClone(from.msg_, &$1, arena_);
161             return *this;
162           }
163 
164           $0(const CProxy& from);
165           $0(const Proxy& from);
166           inline $0& operator=(const CProxy& from) {
167             arena_ = owned_arena_.ptr();
168             msg_ = ($2*)upb_Message_DeepClone(
169                 ::protos::internal::GetInternalMsg(from), &$1, arena_);
170             return *this;
171           }
172           $0($0&& m)
173               : Access(absl::exchange(m.msg_, nullptr),
174                        absl::exchange(m.arena_, nullptr)),
175                 owned_arena_(std::move(m.owned_arena_)) {}
176 
177           $0& operator=($0&& m) {
178             msg_ = absl::exchange(m.msg_, nullptr);
179             arena_ = absl::exchange(m.arena_, nullptr);
180             owned_arena_ = std::move(m.owned_arena_);
181             return *this;
182           }
183       )cc",
184       ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name()),
185       MessageName(descriptor), QualifiedClassName(descriptor));
186 
187   WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output);
188   WriteUsingEnumsInHeader(descriptor, file_enums, output);
189   WriteDefaultInstanceHeader(descriptor, output);
190   WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output);
191   output.Indent();
192   output.Indent();
193   if (descriptor->extension_range_count()) {
194     // for typetrait checking
195     output("using ExtendableType = $0;\n", ClassName(descriptor));
196   }
197   // Note: free function friends that are templates such as ::protos::Parse
198   // require explicit <$2> type parameter in declaration to be able to compile
199   // with gcc otherwise the compiler will fail with
200   // "has not been declared within namespace" error. Even though there is a
201   // namespace qualifier, cross namespace matching fails.
202   output(
203       R"cc(
204         static const upb_MiniTable* minitable();
205         using $0Access::GetInternalArena;
206 
207         private:
208         $0(upb_Message* msg, upb_Arena* arena) : $0Access() {
209           msg_ = ($1*)msg;
210           arena_ = owned_arena_.ptr();
211           upb_Arena_Fuse(arena_, arena);
212         }
213         ::protos::Arena owned_arena_;
214         friend Proxy;
215         friend CProxy;
216         friend absl::StatusOr<$2>(::protos::Parse<$2>(absl::string_view bytes,
217                                                       int options));
218         friend absl::StatusOr<$2>(::protos::Parse<$2>(
219             absl::string_view bytes,
220             const ::protos::ExtensionRegistry& extension_registry,
221             int options));
222         friend upb_Arena* ::protos::internal::GetArena<$0>(const $0& message);
223         friend upb_Arena* ::protos::internal::GetArena<$0>(
224             const ::protos::Ptr<$0>& message);
225         friend $0(::protos::internal::MoveMessage<$0>(upb_Message* msg,
226                                                       upb_Arena* arena));
227       )cc",
228       ClassName(descriptor), MessageName(descriptor),
229       QualifiedClassName(descriptor));
230   output.Outdent();
231   output("};\n\n");
232 }
233 
WriteModelProxyDeclaration(const protobuf::Descriptor * descriptor,Output & output)234 void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
235                                 Output& output) {
236   // Foo::Proxy.
237   output(
238       R"cc(
239         class $0Proxy final : private internal::$0Access {
240          public:
241           $0Proxy() = delete;
242           $0Proxy(const $0Proxy& m) : internal::$0Access() {
243             msg_ = m.msg_;
244             arena_ = m.arena_;
245           }
246           $0Proxy operator=(const $0Proxy& m) {
247             msg_ = m.msg_;
248             arena_ = m.arena_;
249             return *this;
250           }
251           using $0Access::GetInternalArena;
252       )cc",
253       ClassName(descriptor));
254 
255   WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
256                               output);
257   output("\n");
258   output.Indent(1);
259   output(
260       R"cc(
261         private:
262         $0Proxy(void* msg, upb_Arena* arena) : internal::$0Access(($1*)msg, arena) {}
263         friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena));
264         friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>(
265             upb_Message*, upb_Arena*));
266         friend class $0CProxy;
267         friend class $0Access;
268         friend class ::protos::Ptr<$0>;
269         friend class ::protos::Ptr<const $0>;
270         friend upb_Arena* ::protos::internal::GetArena<$2>(const $2& message);
271         friend upb_Arena* ::protos::internal::GetArena<$2>(
272             const ::protos::Ptr<$2>& message);
273         static void Rebind($0Proxy& lhs, const $0Proxy& rhs) {
274           lhs.msg_ = rhs.msg_;
275           lhs.arena_ = rhs.arena_;
276         }
277       )cc",
278       ClassName(descriptor), MessageName(descriptor),
279       QualifiedClassName(descriptor));
280   output.Outdent(1);
281   output("};\n\n");
282 }
283 
WriteModelCProxyDeclaration(const protobuf::Descriptor * descriptor,Output & output)284 void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
285                                  Output& output) {
286   // Foo::CProxy.
287   output(
288       R"cc(
289         class $0CProxy final : private internal::$0Access {
290          public:
291           $0CProxy() = delete;
292           $0CProxy(const $0* m) : internal::$0Access(m->msg_, nullptr) {}
293           $0CProxy($0Proxy m);
294           using $0Access::GetInternalArena;
295       )cc",
296       ClassName(descriptor), MessageName(descriptor));
297 
298   WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
299                               output);
300 
301   output.Indent(1);
302   output(
303       R"cc(
304         private:
305         $0CProxy(void* msg) : internal::$0Access(($1*)msg, nullptr){};
306         friend $0::CProxy(::protos::internal::CreateMessage<$0>(upb_Message* msg));
307         friend class ::protos::Ptr<$0>;
308         friend class ::protos::Ptr<const $0>;
309         static void Rebind($0CProxy& lhs, const $0CProxy& rhs) {
310           lhs.msg_ = rhs.msg_;
311           lhs.arena_ = rhs.arena_;
312         }
313       )cc",
314       ClassName(descriptor), MessageName(descriptor));
315   output.Outdent(1);
316   output("};\n\n");
317 }
318 
WriteDefaultInstanceHeader(const protobuf::Descriptor * message,Output & output)319 void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
320                                 Output& output) {
321   output("  static ::protos::Ptr<const $0> default_instance();\n",
322          ClassName(message));
323 }
324 
WriteMessageImplementation(const protobuf::Descriptor * descriptor,const std::vector<const protobuf::FieldDescriptor * > & file_exts,Output & output)325 void WriteMessageImplementation(
326     const protobuf::Descriptor* descriptor,
327     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
328     Output& output) {
329   bool message_is_map_entry = descriptor->options().map_entry();
330   if (!message_is_map_entry) {
331     // Constructor.
332     output(
333         R"cc(
334           $0::$0() : $0Access() {
335             arena_ = owned_arena_.ptr();
336             msg_ = $1_new(arena_);
337           }
338           $0::$0(const $0& from) : $0Access() {
339             arena_ = owned_arena_.ptr();
340             msg_ = ($1*)upb_Message_DeepClone(from.msg_, &$2, arena_);
341           }
342           $0::$0(const CProxy& from) : $0Access() {
343             arena_ = owned_arena_.ptr();
344             msg_ = ($1*)upb_Message_DeepClone(
345                 ::protos::internal::GetInternalMsg(from), &$2, arena_);
346           }
347           $0::$0(const Proxy& from) : $0(static_cast<const CProxy&>(from)) {}
348           internal::$0CProxy::$0CProxy($0Proxy m) : $0Access() {
349             arena_ = m.arena_;
350             msg_ = ($1*)::protos::internal::GetInternalMsg(m);
351           }
352         )cc",
353         ClassName(descriptor), MessageName(descriptor),
354         ::upbc::MessageInit(descriptor->full_name()),
355         QualifiedClassName(descriptor));
356     output("\n");
357     // Minitable
358     output(
359         R"cc(
360           const upb_MiniTable* $0::minitable() { return &$1; }
361         )cc",
362         ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name()));
363     output("\n");
364   }
365 
366   WriteAccessorsInSource(descriptor, output);
367 
368   if (!message_is_map_entry) {
369     output(
370         R"cc(
371           struct $0DefaultTypeInternal {
372             $1* msg;
373           };
374           $0DefaultTypeInternal _$0_default_instance_ =
375               $0DefaultTypeInternal{$1_new(upb_Arena_New())};
376         )cc",
377         ClassName(descriptor), MessageName(descriptor));
378 
379     output(
380         R"cc(
381           ::protos::Ptr<const $0> $0::default_instance() {
382             return ::protos::internal::CreateMessage<$0>(
383                 (upb_Message *)_$0_default_instance_.msg);
384           }
385         )cc",
386         ClassName(descriptor));
387 
388     WriteExtensionIdentifiersImplementation(descriptor, file_exts, output);
389   }
390 }
391 
WriteInternalForwardDeclarationsInHeader(const protobuf::Descriptor * message,Output & output)392 void WriteInternalForwardDeclarationsInHeader(
393     const protobuf::Descriptor* message, Output& output) {
394   // Write declaration for internal re-usable default_instance without
395   // leaking implementation.
396   output(
397       R"cc(
398         struct $0DefaultTypeInternal;
399         extern $0DefaultTypeInternal _$0_default_instance_;
400       )cc",
401       ClassName(message));
402 }
403 
WriteExtensionIdentifiersInClassHeader(const protobuf::Descriptor * message,const std::vector<const protobuf::FieldDescriptor * > & file_exts,Output & output)404 void WriteExtensionIdentifiersInClassHeader(
405     const protobuf::Descriptor* message,
406     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
407     Output& output) {
408   for (auto* ext : file_exts) {
409     if (ext->extension_scope() &&
410         ext->extension_scope()->full_name() == message->full_name()) {
411       WriteExtensionIdentifierHeader(ext, output);
412     }
413   }
414 }
415 
WriteExtensionIdentifiersImplementation(const protobuf::Descriptor * message,const std::vector<const protobuf::FieldDescriptor * > & file_exts,Output & output)416 void WriteExtensionIdentifiersImplementation(
417     const protobuf::Descriptor* message,
418     const std::vector<const protobuf::FieldDescriptor*>& file_exts,
419     Output& output) {
420   for (auto* ext : file_exts) {
421     if (ext->extension_scope() &&
422         ext->extension_scope()->full_name() == message->full_name()) {
423       WriteExtensionIdentifier(ext, output);
424     }
425   }
426 }
427 
WriteUsingEnumsInHeader(const protobuf::Descriptor * message,const std::vector<const protobuf::EnumDescriptor * > & file_enums,Output & output)428 void WriteUsingEnumsInHeader(
429     const protobuf::Descriptor* message,
430     const std::vector<const protobuf::EnumDescriptor*>& file_enums,
431     Output& output) {
432   for (auto* enum_descriptor : file_enums) {
433     std::string enum_type_name = EnumTypeName(enum_descriptor);
434     std::string enum_resolved_type_name =
435         enum_descriptor->file()->package().empty() &&
436                 enum_descriptor->containing_type() == nullptr
437             ? absl::StrCat(kNoPackageNamePrefix,
438                            ToCIdent(enum_descriptor->name()))
439             : enum_type_name;
440     if (enum_descriptor->containing_type() == nullptr ||
441         enum_descriptor->containing_type()->full_name() !=
442             message->full_name()) {
443       continue;
444     }
445     output("using $0", enum_descriptor->name());
446     if (enum_descriptor->options().deprecated()) {
447       output(" ABSL_DEPRECATED(\"Proto enum $0\")", enum_descriptor->name());
448     }
449     output(" = $0;", enum_resolved_type_name);
450     output("\n");
451     int value_count = enum_descriptor->value_count();
452     for (int i = 0; i < value_count; i++) {
453       output("static constexpr $0 $1", enum_descriptor->name(),
454              enum_descriptor->value(i)->name());
455       if (enum_descriptor->options().deprecated() ||
456           enum_descriptor->value(i)->options().deprecated()) {
457         output(" ABSL_DEPRECATED(\"Proto enum value $0\") ",
458                enum_descriptor->value(i)->name());
459       }
460       output(" = $0;\n", EnumValueSymbolInNameSpace(enum_descriptor,
461                                                     enum_descriptor->value(i)));
462     }
463   }
464 }
465 
466 }  // namespace protos_generator
467