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